// File:src/Three.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

var THREE = { REVISION: '73' };

//

if ( typeof define === 'function' && define.amd ) {

    define( 'three', THREE );

} else if ( 'undefined' !== typeof exports && 'undefined' !== typeof module ) {

    module.exports = THREE;

}


// polyfills

if ( self.requestAnimationFrame === undefined || self.cancelAnimationFrame === undefined ) {

    // Missing in Android stock browser.

    ( function () {

        var lastTime = 0;
        var vendors = [ 'ms', 'moz', 'webkit', 'o' ];

        for ( var x = 0; x < vendors.length && ! self.requestAnimationFrame; ++ x ) {

            self.requestAnimationFrame = self[ vendors[ x ] + 'RequestAnimationFrame' ];
            self.cancelAnimationFrame = self[ vendors[ x ] + 'CancelAnimationFrame' ] || self[ vendors[ x ] + 'CancelRequestAnimationFrame' ];

        }

        if ( self.requestAnimationFrame === undefined && self.setTimeout !== undefined ) {

            self.requestAnimationFrame = function ( callback ) {

                var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
                var id = self.setTimeout( function () {

                    callback( currTime + timeToCall );

                }, timeToCall );
                lastTime = currTime + timeToCall;
                return id;

            };

        }

        if ( self.cancelAnimationFrame === undefined && self.clearTimeout !== undefined ) {

            self.cancelAnimationFrame = function ( id ) {

                self.clearTimeout( id );

            };

        }

    } )();

}

//

if ( self.performance === undefined ) {

    self.performance = {};

}

if ( self.performance.now === undefined ) {

    ( function () {

        var start = Date.now();

        self.performance.now = function () {

            return Date.now() - start;

        }

    } )();

}

//

if ( Number.EPSILON === undefined ) {

    Number.EPSILON = Math.pow( 2, -52 );

}

//

if ( Math.sign === undefined ) {

    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign

    Math.sign = function ( x ) {

        return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x;

    };

}

if ( Function.prototype.name === undefined && Object.defineProperty !== undefined ) {

    // Missing in IE9-11.
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name

    Object.defineProperty( Function.prototype, 'name', {

        get: function () {

            return this.toString().match( /^\s*function\s*(\S*)\s*\(/ )[ 1 ];

        }

    } );

}

// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button

THREE.MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };

// GL STATE CONSTANTS

THREE.CullFaceNone = 0;
THREE.CullFaceBack = 1;
THREE.CullFaceFront = 2;
THREE.CullFaceFrontBack = 3;

THREE.FrontFaceDirectionCW = 0;
THREE.FrontFaceDirectionCCW = 1;

// SHADOWING TYPES

THREE.BasicShadowMap = 0;
THREE.PCFShadowMap = 1;
THREE.PCFSoftShadowMap = 2;

// MATERIAL CONSTANTS

// side

THREE.FrontSide = 0;
THREE.BackSide = 1;
THREE.DoubleSide = 2;

// shading

THREE.FlatShading = 1;
THREE.SmoothShading = 2;

// colors

THREE.NoColors = 0;
THREE.FaceColors = 1;
THREE.VertexColors = 2;

// blending modes

THREE.NoBlending = 0;
THREE.NormalBlending = 1;
THREE.AdditiveBlending = 2;
THREE.SubtractiveBlending = 3;
THREE.MultiplyBlending = 4;
THREE.CustomBlending = 5;

// custom blending equations
// (numbers start from 100 not to clash with other
// mappings to OpenGL constants defined in Texture.js)

THREE.AddEquation = 100;
THREE.SubtractEquation = 101;
THREE.ReverseSubtractEquation = 102;
THREE.MinEquation = 103;
THREE.MaxEquation = 104;

// custom blending destination factors

THREE.ZeroFactor = 200;
THREE.OneFactor = 201;
THREE.SrcColorFactor = 202;
THREE.OneMinusSrcColorFactor = 203;
THREE.SrcAlphaFactor = 204;
THREE.OneMinusSrcAlphaFactor = 205;
THREE.DstAlphaFactor = 206;
THREE.OneMinusDstAlphaFactor = 207;

// custom blending source factors

//THREE.ZeroFactor = 200;
//THREE.OneFactor = 201;
//THREE.SrcAlphaFactor = 204;
//THREE.OneMinusSrcAlphaFactor = 205;
//THREE.DstAlphaFactor = 206;
//THREE.OneMinusDstAlphaFactor = 207;
THREE.DstColorFactor = 208;
THREE.OneMinusDstColorFactor = 209;
THREE.SrcAlphaSaturateFactor = 210;

// depth modes

THREE.NeverDepth = 0;
THREE.AlwaysDepth = 1;
THREE.LessDepth = 2;
THREE.LessEqualDepth = 3;
THREE.EqualDepth = 4;
THREE.GreaterEqualDepth = 5;
THREE.GreaterDepth = 6;
THREE.NotEqualDepth = 7;


// TEXTURE CONSTANTS

THREE.MultiplyOperation = 0;
THREE.MixOperation = 1;
THREE.AddOperation = 2;

// Mapping modes

THREE.UVMapping = 300;

THREE.CubeReflectionMapping = 301;
THREE.CubeRefractionMapping = 302;

THREE.EquirectangularReflectionMapping = 303;
THREE.EquirectangularRefractionMapping = 304;

THREE.SphericalReflectionMapping = 305;

// Wrapping modes

THREE.RepeatWrapping = 1000;
THREE.ClampToEdgeWrapping = 1001;
THREE.MirroredRepeatWrapping = 1002;

// Filters

THREE.NearestFilter = 1003;
THREE.NearestMipMapNearestFilter = 1004;
THREE.NearestMipMapLinearFilter = 1005;
THREE.LinearFilter = 1006;
THREE.LinearMipMapNearestFilter = 1007;
THREE.LinearMipMapLinearFilter = 1008;

// Data types

THREE.UnsignedByteType = 1009;
THREE.ByteType = 1010;
THREE.ShortType = 1011;
THREE.UnsignedShortType = 1012;
THREE.IntType = 1013;
THREE.UnsignedIntType = 1014;
THREE.FloatType = 1015;
THREE.HalfFloatType = 1025;

// Pixel types

//THREE.UnsignedByteType = 1009;
THREE.UnsignedShort4444Type = 1016;
THREE.UnsignedShort5551Type = 1017;
THREE.UnsignedShort565Type = 1018;

// Pixel formats

THREE.AlphaFormat = 1019;
THREE.RGBFormat = 1020;
THREE.RGBAFormat = 1021;
THREE.LuminanceFormat = 1022;
THREE.LuminanceAlphaFormat = 1023;
// THREE.RGBEFormat handled as THREE.RGBAFormat in shaders
THREE.RGBEFormat = THREE.RGBAFormat; //1024;

// DDS / ST3C Compressed texture formats

THREE.RGB_S3TC_DXT1_Format = 2001;
THREE.RGBA_S3TC_DXT1_Format = 2002;
THREE.RGBA_S3TC_DXT3_Format = 2003;
THREE.RGBA_S3TC_DXT5_Format = 2004;


// PVRTC compressed texture formats

THREE.RGB_PVRTC_4BPPV1_Format = 2100;
THREE.RGB_PVRTC_2BPPV1_Format = 2101;
THREE.RGBA_PVRTC_4BPPV1_Format = 2102;
THREE.RGBA_PVRTC_2BPPV1_Format = 2103;

// Loop styles for AnimationAction

THREE.LoopOnce = 2200;
THREE.LoopRepeat = 2201;
THREE.LoopPingPong = 2202;

// DEPRECATED

THREE.Projector = function () {

    console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' );

    this.projectVector = function ( vector, camera ) {

        console.warn( 'THREE.Projector: .projectVector() is now vector.project().' );
        vector.project( camera );

    };

    this.unprojectVector = function ( vector, camera ) {

        console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' );
        vector.unproject( camera );

    };

    this.pickingRay = function ( vector, camera ) {

        console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' );

    };

};

THREE.CanvasRenderer = function () {

    console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' );

    this.domElement = document.createElement( 'canvas' );
    this.clear = function () {};
    this.render = function () {};
    this.setClearColor = function () {};
    this.setSize = function () {};

};

// File:src/math/Color.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.Color = function ( color ) {

    if ( arguments.length === 3 ) {

        return this.fromArray( arguments );

    }

    return this.set( color );

};

THREE.Color.prototype = {

    constructor: THREE.Color,

    r: 1, g: 1, b: 1,

    set: function ( value ) {

        if ( value instanceof THREE.Color ) {

            this.copy( value );

        } else if ( typeof value === 'number' ) {

            this.setHex( value );

        } else if ( typeof value === 'string' ) {

            this.setStyle( value );

        }

        return this;

    },

    setHex: function ( hex ) {

        hex = Math.floor( hex );

        this.r = ( hex >> 16 & 255 ) / 255;
        this.g = ( hex >> 8 & 255 ) / 255;
        this.b = ( hex & 255 ) / 255;

        return this;

    },

    setRGB: function ( r, g, b ) {

        this.r = r;
        this.g = g;
        this.b = b;

        return this;

    },

    setHSL: function () {

        function hue2rgb( p, q, t ) {

            if ( t < 0 ) t += 1;
            if ( t > 1 ) t -= 1;
            if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;
            if ( t < 1 / 2 ) return q;
            if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );
            return p;

        }

        return function ( h, s, l ) {

            // h,s,l ranges are in 0.0 - 1.0
            h = THREE.Math.euclideanModulo( h, 1 );
            s = THREE.Math.clamp( s, 0, 1 );
            l = THREE.Math.clamp( l, 0, 1 );

            if ( s === 0 ) {

                this.r = this.g = this.b = l;

            } else {

                var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
                var q = ( 2 * l ) - p;

                this.r = hue2rgb( q, p, h + 1 / 3 );
                this.g = hue2rgb( q, p, h );
                this.b = hue2rgb( q, p, h - 1 / 3 );

            }

            return this;

        };

    }(),

    setStyle: function ( style ) {

        function handleAlpha( string ) {

            if ( string === undefined ) return;

            if ( parseFloat( string ) < 1 ) {

                console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );

            }

        }


        var m;

        if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) {

            // rgb / hsl

            var color;
            var name = m[ 1 ];
            var components = m[ 2 ];

            switch ( name ) {

                case 'rgb':
                case 'rgba':

                    if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {

                        // rgb(255,0,0) rgba(255,0,0,0.5)
                        this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;
                        this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
                        this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;

                        handleAlpha( color[ 5 ] );

                        return this;

                    }

                    if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {

                        // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)
                        this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;
                        this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
                        this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;

                        handleAlpha( color[ 5 ] );

                        return this;

                    }

                    break;

                case 'hsl':
                case 'hsla':

                    if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {

                        // hsl(120,50%,50%) hsla(120,50%,50%,0.5)
                        var h = parseFloat( color[ 1 ] ) / 360;
                        var s = parseInt( color[ 2 ], 10 ) / 100;
                        var l = parseInt( color[ 3 ], 10 ) / 100;

                        handleAlpha( color[ 5 ] );

                        return this.setHSL( h, s, l );

                    }

                    break;

            }

        } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) {

            // hex color

            var hex = m[ 1 ];
            var size = hex.length;

            if ( size === 3 ) {

                // #ff0
                this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;
                this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;
                this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;

                return this;

            } else if ( size === 6 ) {

                // #ff0000
                this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;
                this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;
                this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;

                return this;

            }

        }

        if ( style && style.length > 0 ) {

            // color keywords
            var hex = THREE.ColorKeywords[ style ];

            if ( hex !== undefined ) {

                // red
                this.setHex( hex );

            } else {

                // unknown color
                console.warn( 'THREE.Color: Unknown color ' + style );

            }

        }

        return this;

    },

    clone: function () {

        return new this.constructor( this.r, this.g, this.b );

    },

    copy: function ( color ) {

        this.r = color.r;
        this.g = color.g;
        this.b = color.b;

        return this;

    },

    copyGammaToLinear: function ( color, gammaFactor ) {

        if ( gammaFactor === undefined ) gammaFactor = 2.0;

        this.r = Math.pow( color.r, gammaFactor );
        this.g = Math.pow( color.g, gammaFactor );
        this.b = Math.pow( color.b, gammaFactor );

        return this;

    },

    copyLinearToGamma: function ( color, gammaFactor ) {

        if ( gammaFactor === undefined ) gammaFactor = 2.0;

        var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;

        this.r = Math.pow( color.r, safeInverse );
        this.g = Math.pow( color.g, safeInverse );
        this.b = Math.pow( color.b, safeInverse );

        return this;

    },

    convertGammaToLinear: function () {

        var r = this.r, g = this.g, b = this.b;

        this.r = r * r;
        this.g = g * g;
        this.b = b * b;

        return this;

    },

    convertLinearToGamma: function () {

        this.r = Math.sqrt( this.r );
        this.g = Math.sqrt( this.g );
        this.b = Math.sqrt( this.b );

        return this;

    },

    getHex: function () {

        return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;

    },

    getHexString: function () {

        return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );

    },

    getHSL: function ( optionalTarget ) {

        // h,s,l ranges are in 0.0 - 1.0

        var hsl = optionalTarget || { h: 0, s: 0, l: 0 };

        var r = this.r, g = this.g, b = this.b;

        var max = Math.max( r, g, b );
        var min = Math.min( r, g, b );

        var hue, saturation;
        var lightness = ( min + max ) / 2.0;

        if ( min === max ) {

            hue = 0;
            saturation = 0;

        } else {

            var delta = max - min;

            saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );

            switch ( max ) {

                case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
                case g: hue = ( b - r ) / delta + 2; break;
                case b: hue = ( r - g ) / delta + 4; break;

            }

            hue /= 6;

        }

        hsl.h = hue;
        hsl.s = saturation;
        hsl.l = lightness;

        return hsl;

    },

    getStyle: function () {

        return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';

    },

    offsetHSL: function ( h, s, l ) {

        var hsl = this.getHSL();

        hsl.h += h; hsl.s += s; hsl.l += l;

        this.setHSL( hsl.h, hsl.s, hsl.l );

        return this;

    },

    add: function ( color ) {

        this.r += color.r;
        this.g += color.g;
        this.b += color.b;

        return this;

    },

    addColors: function ( color1, color2 ) {

        this.r = color1.r + color2.r;
        this.g = color1.g + color2.g;
        this.b = color1.b + color2.b;

        return this;

    },

    addScalar: function ( s ) {

        this.r += s;
        this.g += s;
        this.b += s;

        return this;

    },

    multiply: function ( color ) {

        this.r *= color.r;
        this.g *= color.g;
        this.b *= color.b;

        return this;

    },

    multiplyScalar: function ( s ) {

        this.r *= s;
        this.g *= s;
        this.b *= s;

        return this;

    },

    lerp: function ( color, alpha ) {

        this.r += ( color.r - this.r ) * alpha;
        this.g += ( color.g - this.g ) * alpha;
        this.b += ( color.b - this.b ) * alpha;

        return this;

    },

    equals: function ( c ) {

        return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );

    },

    fromArray: function ( array, offset ) {

        if ( offset === undefined ) offset = 0;

        this.r = array[ offset ];
        this.g = array[ offset + 1 ];
        this.b = array[ offset + 2 ];

        return this;

    },

    toArray: function ( array, offset ) {

        if ( array === undefined ) array = [];
        if ( offset === undefined ) offset = 0;

        array[ offset ] = this.r;
        array[ offset + 1 ] = this.g;
        array[ offset + 2 ] = this.b;

        return array;

    }

};

THREE.ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,
    'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,
    'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,
    'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,
    'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,
    'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,
    'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,
    'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,
    'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,
    'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,
    'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,
    'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,
    'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,
    'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,
    'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,
    'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,
    'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
    'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
    'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
    'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
    'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
    'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
    'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
    'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };

// File:src/math/Quaternion.js

/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 * @author WestLangley / http://github.com/WestLangley
 * @author bhouston / http://clara.io
 */

THREE.Quaternion = function ( x, y, z, w ) {

    this._x = x || 0;
    this._y = y || 0;
    this._z = z || 0;
    this._w = ( w !== undefined ) ? w : 1;

};

THREE.Quaternion.prototype = {

    constructor: THREE.Quaternion,

    get x () {

        return this._x;

    },

    set x ( value ) {

        this._x = value;
        this.onChangeCallback();

    },

    get y () {

        return this._y;

    },

    set y ( value ) {

        this._y = value;
        this.onChangeCallback();

    },

    get z () {

        return this._z;

    },

    set z ( value ) {

        this._z = value;
        this.onChangeCallback();

    },

    get w () {

        return this._w;

    },

    set w ( value ) {

        this._w = value;
        this.onChangeCallback();

    },

    set: function ( x, y, z, w ) {

        this._x = x;
        this._y = y;
        this._z = z;
        this._w = w;

        this.onChangeCallback();

        return this;

    },

    clone: function () {

        return new this.constructor( this._x, this._y, this._z, this._w );

    },

    copy: function ( quaternion ) {

        this._x = quaternion.x;
        this._y = quaternion.y;
        this._z = quaternion.z;
        this._w = quaternion.w;

        this.onChangeCallback();

        return this;

    },

    setFromEuler: function ( euler, update ) {

        if ( euler instanceof THREE.Euler === false ) {

            throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );

        }

        // http://www.mathworks.com/matlabcentral/fileexchange/
        // 	20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
        //	content/SpinCalc.m

        var c1 = Math.cos( euler._x / 2 );
        var c2 = Math.cos( euler._y / 2 );
        var c3 = Math.cos( euler._z / 2 );
        var s1 = Math.sin( euler._x / 2 );
        var s2 = Math.sin( euler._y / 2 );
        var s3 = Math.sin( euler._z / 2 );

        var order = euler.order;

        if ( order === 'XYZ' ) {

            this._x = s1 * c2 * c3 + c1 * s2 * s3;
            this._y = c1 * s2 * c3 - s1 * c2 * s3;
            this._z = c1 * c2 * s3 + s1 * s2 * c3;
            this._w = c1 * c2 * c3 - s1 * s2 * s3;

        } else if ( order === 'YXZ' ) {

            this._x = s1 * c2 * c3 + c1 * s2 * s3;
            this._y = c1 * s2 * c3 - s1 * c2 * s3;
            this._z = c1 * c2 * s3 - s1 * s2 * c3;
            this._w = c1 * c2 * c3 + s1 * s2 * s3;

        } else if ( order === 'ZXY' ) {

            this._x = s1 * c2 * c3 - c1 * s2 * s3;
            this._y = c1 * s2 * c3 + s1 * c2 * s3;
            this._z = c1 * c2 * s3 + s1 * s2 * c3;
            this._w = c1 * c2 * c3 - s1 * s2 * s3;

        } else if ( order === 'ZYX' ) {

            this._x = s1 * c2 * c3 - c1 * s2 * s3;
            this._y = c1 * s2 * c3 + s1 * c2 * s3;
            this._z = c1 * c2 * s3 - s1 * s2 * c3;
            this._w = c1 * c2 * c3 + s1 * s2 * s3;

        } else if ( order === 'YZX' ) {

            this._x = s1 * c2 * c3 + c1 * s2 * s3;
            this._y = c1 * s2 * c3 + s1 * c2 * s3;
            this._z = c1 * c2 * s3 - s1 * s2 * c3;
            this._w = c1 * c2 * c3 - s1 * s2 * s3;

        } else if ( order === 'XZY' ) {

            this._x = s1 * c2 * c3 - c1 * s2 * s3;
            this._y = c1 * s2 * c3 - s1 * c2 * s3;
            this._z = c1 * c2 * s3 + s1 * s2 * c3;
            this._w = c1 * c2 * c3 + s1 * s2 * s3;

        }

        if ( update !== false ) this.onChangeCallback();

        return this;

    },

    setFromAxisAngle: function ( axis, angle ) {

        // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm

        // assumes axis is normalized

        var halfAngle = angle / 2, s = Math.sin( halfAngle );

        this._x = axis.x * s;
        this._y = axis.y * s;
        this._z = axis.z * s;
        this._w = Math.cos( halfAngle );

        this.onChangeCallback();

        return this;

    },

    setFromRotationMatrix: function ( m ) {

        // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm

        // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)

        var te = m.elements,

            m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
            m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
            m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],

            trace = m11 + m22 + m33,
            s;

        if ( trace > 0 ) {

            s = 0.5 / Math.sqrt( trace + 1.0 );

            this._w = 0.25 / s;
            this._x = ( m32 - m23 ) * s;
            this._y = ( m13 - m31 ) * s;
            this._z = ( m21 - m12 ) * s;

        } else if ( m11 > m22 && m11 > m33 ) {

            s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );

            this._w = ( m32 - m23 ) / s;
            this._x = 0.25 * s;
            this._y = ( m12 + m21 ) / s;
            this._z = ( m13 + m31 ) / s;

        } else if ( m22 > m33 ) {

            s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );

            this._w = ( m13 - m31 ) / s;
            this._x = ( m12 + m21 ) / s;
            this._y = 0.25 * s;
            this._z = ( m23 + m32 ) / s;

        } else {

            s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );

            this._w = ( m21 - m12 ) / s;
            this._x = ( m13 + m31 ) / s;
            this._y = ( m23 + m32 ) / s;
            this._z = 0.25 * s;

        }

        this.onChangeCallback();

        return this;

    },

    setFromUnitVectors: function () {

        // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final

        // assumes direction vectors vFrom and vTo are normalized

        var v1, r;

        var EPS = 0.000001;

        return function ( vFrom, vTo ) {

            if ( v1 === undefined ) v1 = new THREE.Vector3();

            r = vFrom.dot( vTo ) + 1;

            if ( r < EPS ) {

                r = 0;

                if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {

                    v1.set( - vFrom.y, vFrom.x, 0 );

                } else {

                    v1.set( 0, - vFrom.z, vFrom.y );

                }

            } else {

                v1.crossVectors( vFrom, vTo );

            }

            this._x = v1.x;
            this._y = v1.y;
            this._z = v1.z;
            this._w = r;

            this.normalize();

            return this;

        }

    }(),

    inverse: function () {

        this.conjugate().normalize();

        return this;

    },

    conjugate: function () {

        this._x *= - 1;
        this._y *= - 1;
        this._z *= - 1;

        this.onChangeCallback();

        return this;

    },

    dot: function ( v ) {

        return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;

    },

    lengthSq: function () {

        return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;

    },

    length: function () {

        return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );

    },

    normalize: function () {

        var l = this.length();

        if ( l === 0 ) {

            this._x = 0;
            this._y = 0;
            this._z = 0;
            this._w = 1;

        } else {

            l = 1 / l;

            this._x = this._x * l;
            this._y = this._y * l;
            this._z = this._z * l;
            this._w = this._w * l;

        }

        this.onChangeCallback();

        return this;

    },

    multiply: function ( q, p ) {

        if ( p !== undefined ) {

            console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
            return this.multiplyQuaternions( q, p );

        }

        return this.multiplyQuaternions( this, q );

    },

    multiplyQuaternions: function ( a, b ) {

        // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm

        var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
        var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;

        this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
        this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
        this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
        this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;

        this.onChangeCallback();

        return this;

    },

    multiplyVector3: function ( vector ) {

        console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
        return vector.applyQuaternion( this );

    },

    slerp: function ( qb, t ) {

        if ( t === 0 ) return this;
        if ( t === 1 ) return this.copy( qb );

        var x = this._x, y = this._y, z = this._z, w = this._w;

        // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/

        var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;

        if ( cosHalfTheta < 0 ) {

            this._w = - qb._w;
            this._x = - qb._x;
            this._y = - qb._y;
            this._z = - qb._z;

            cosHalfTheta = - cosHalfTheta;

        } else {

            this.copy( qb );

        }

        if ( cosHalfTheta >= 1.0 ) {

            this._w = w;
            this._x = x;
            this._y = y;
            this._z = z;

            return this;

        }

        var halfTheta = Math.acos( cosHalfTheta );
        var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );

        if ( Math.abs( sinHalfTheta ) < 0.001 ) {

            this._w = 0.5 * ( w + this._w );
            this._x = 0.5 * ( x + this._x );
            this._y = 0.5 * ( y + this._y );
            this._z = 0.5 * ( z + this._z );

            return this;

        }

        var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
            ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;

        this._w = ( w * ratioA + this._w * ratioB );
        this._x = ( x * ratioA + this._x * ratioB );
        this._y = ( y * ratioA + this._y * ratioB );
        this._z = ( z * ratioA + this._z * ratioB );

        this.onChangeCallback();

        return this;

    },

    equals: function ( quaternion ) {

        return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );

    },

    fromArray: function ( array, offset ) {

        if ( offset === undefined ) offset = 0;

        this._x = array[ offset ];
        this._y = array[ offset + 1 ];
        this._z = array[ offset + 2 ];
        this._w = array[ offset + 3 ];

        this.onChangeCallback();

        return this;

    },

    toArray: function ( array, offset ) {

        if ( array === undefined ) array = [];
        if ( offset === undefined ) offset = 0;

        array[ offset ] = this._x;
        array[ offset + 1 ] = this._y;
        array[ offset + 2 ] = this._z;
        array[ offset + 3 ] = this._w;

        return array;

    },

    onChange: function ( callback ) {

        this.onChangeCallback = callback;

        return this;

    },

    onChangeCallback: function () {}

};

THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {

    return qm.copy( qa ).slerp( qb, t );

};

// File:src/math/Vector2.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author philogb / http://blog.thejit.org/
 * @author egraether / http://egraether.com/
 * @author zz85 / http://www.lab4games.net/zz85/blog
 */

THREE.Vector2 = function ( x, y ) {

    this.x = x || 0;
    this.y = y || 0;

};

THREE.Vector2.prototype = {

    constructor: THREE.Vector2,

    get width() { return this.x },
    set width( value ) { this.x = value },

    get height() { return this.y },
    set height( value ) { this.y = value },

    //

    set: function ( x, y ) {

        this.x = x;
        this.y = y;

        return this;

    },

    setX: function ( x ) {

        this.x = x;

        return this;

    },

    setY: function ( y ) {

        this.y = y;

        return this;

    },

    setComponent: function ( index, value ) {

        switch ( index ) {

            case 0: this.x = value; break;
            case 1: this.y = value; break;
            default: throw new Error( 'index is out of range: ' + index );

        }

    },

    getComponent: function ( index ) {

        switch ( index ) {

            case 0: return this.x;
            case 1: return this.y;
            default: throw new Error( 'index is out of range: ' + index );

        }

    },

    clone: function () {

        return new this.constructor( this.x, this.y );

    },

    copy: function ( v ) {

        this.x = v.x;
        this.y = v.y;

        return this;

    },

    add: function ( v, w ) {

        if ( w !== undefined ) {

            console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
            return this.addVectors( v, w );

        }

        this.x += v.x;
        this.y += v.y;

        return this;

    },

    addScalar: function ( s ) {

        this.x += s;
        this.y += s;

        return this;

    },

    addVectors: function ( a, b ) {

        this.x = a.x + b.x;
        this.y = a.y + b.y;

        return this;

    },

    addScaledVector: function ( v, s ) {

        this.x += v.x * s;
        this.y += v.y * s;

        return this;

    },

    sub: function ( v, w ) {

        if ( w !== undefined ) {

            console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
            return this.subVectors( v, w );

        }

        this.x -= v.x;
        this.y -= v.y;

        return this;

    },

    subScalar: function ( s ) {

        this.x -= s;
        this.y -= s;

        return this;

    },

    subVectors: function ( a, b ) {

        this.x = a.x - b.x;
        this.y = a.y - b.y;

        return this;

    },

    multiply: function ( v ) {

        this.x *= v.x;
        this.y *= v.y;

        return this;

    },

    multiplyScalar: function ( scalar ) {

        if ( isFinite( scalar ) ) {
            this.x *= scalar;
            this.y *= scalar;
        } else {
            this.x = 0;
            this.y = 0;
        }

        return this;

    },

    divide: function ( v ) {

        this.x /= v.x;
        this.y /= v.y;

        return this;

    },

    divideScalar: function ( scalar ) {

        return this.multiplyScalar( 1 / scalar );

    },

    min: function ( v ) {

        this.x = Math.min( this.x, v.x );
        this.y = Math.min( this.y, v.y );

        return this;

    },

    max: function ( v ) {

        this.x = Math.max( this.x, v.x );
        this.y = Math.max( this.y, v.y );

        return this;

    },

    clamp: function ( min, max ) {

        // This function assumes min < max, if this assumption isn't true it will not operate correctly

        this.x = Math.max( min.x, Math.min( max.x, this.x ) );
        this.y = Math.max( min.y, Math.min( max.y, this.y ) );

        return this;

    },

    clampScalar: function () {

        var min, max;

        return function clampScalar( minVal, maxVal ) {

            if ( min === undefined ) {

                min = new THREE.Vector2();
                max = new THREE.Vector2();

            }

            min.set( minVal, minVal );
            max.set( maxVal, maxVal );

            return this.clamp( min, max );

        };

    }(),

    clampLength: function ( min, max ) {

        var length = this.length();

        this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length );

        return this;

    },

    floor: function () {

        this.x = Math.floor( this.x );
        this.y = Math.floor( this.y );

        return this;

    },

    ceil: function () {

        this.x = Math.ceil( this.x );
        this.y = Math.ceil( this.y );

        return this;

    },

    round: function () {

        this.x = Math.round( this.x );
        this.y = Math.round( this.y );

        return this;

    },

    roundToZero: function () {

        this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
        this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );

        return this;

    },

    negate: function () {

        this.x = - this.x;
        this.y = - this.y;

        return this;

    },

    dot: function ( v ) {

        return this.x * v.x + this.y * v.y;

    },

    lengthSq: function () {

        return this.x * this.x + this.y * this.y;

    },

    length: function () {

        return Math.sqrt( this.x * this.x + this.y * this.y );

    },

    lengthManhattan: function() {

        return Math.abs( this.x ) + Math.abs( this.y );

    },

    normalize: function () {

        return this.divideScalar( this.length() );

    },

    distanceTo: function ( v ) {

        return Math.sqrt( this.distanceToSquared( v ) );

    },

    distanceToSquared: function ( v ) {

        var dx = this.x - v.x, dy = this.y - v.y;
        return dx * dx + dy * dy;

    },

    setLength: function ( length ) {

        return this.multiplyScalar( length / this.length() );

    },

    lerp: function ( v, alpha ) {

        this.x += ( v.x - this.x ) * alpha;
        this.y += ( v.y - this.y ) * alpha;

        return this;

    },

    lerpVectors: function ( v1, v2, alpha ) {

        this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );

        return this;

    },

    equals: function ( v ) {

        return ( ( v.x === this.x ) && ( v.y === this.y ) );

    },

    fromArray: function ( array, offset ) {

        if ( offset === undefined ) offset = 0;

        this.x = array[ offset ];
        this.y = array[ offset + 1 ];

        return this;

    },

    toArray: function ( array, offset ) {

        if ( array === undefined ) array = [];
        if ( offset === undefined ) offset = 0;

        array[ offset ] = this.x;
        array[ offset + 1 ] = this.y;

        return array;

    },

    fromAttribute: function ( attribute, index, offset ) {

        if ( offset === undefined ) offset = 0;

        index = index * attribute.itemSize + offset;

        this.x = attribute.array[ index ];
        this.y = attribute.array[ index + 1 ];

        return this;

    },

    rotateAround: function ( center, angle ) {

        var c = Math.cos( angle ), s = Math.sin( angle );

        var x = this.x - center.x;
        var y = this.y - center.y;

        this.x = x * c - y * s + center.x;
        this.y = x * s + y * c + center.y;

        return this;

    }

};

// File:src/math/Vector3.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author *kile / http://kile.stravaganza.org/
 * @author philogb / http://blog.thejit.org/
 * @author mikael emtinger / http://gomo.se/
 * @author egraether / http://egraether.com/
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.Vector3 = function ( x, y, z ) {

    this.x = x || 0;
    this.y = y || 0;
    this.z = z || 0;

};

THREE.Vector3.prototype = {

    constructor: THREE.Vector3,

    set: function ( x, y, z ) {

        this.x = x;
        this.y = y;
        this.z = z;

        return this;

    },

    setX: function ( x ) {

        this.x = x;

        return this;

    },

    setY: function ( y ) {

        this.y = y;

        return this;

    },

    setZ: function ( z ) {

        this.z = z;

        return this;

    },

    setComponent: function ( index, value ) {

        switch ( index ) {

            case 0: this.x = value; break;
            case 1: this.y = value; break;
            case 2: this.z = value; break;
            default: throw new Error( 'index is out of range: ' + index );

        }

    },

    getComponent: function ( index ) {

        switch ( index ) {

            case 0: return this.x;
            case 1: return this.y;
            case 2: return this.z;
            default: throw new Error( 'index is out of range: ' + index );

        }

    },

    clone: function () {

        return new this.constructor( this.x, this.y, this.z );

    },

    copy: function ( v ) {

        this.x = v.x;
        this.y = v.y;
        this.z = v.z;

        return this;

    },

    add: function ( v, w ) {

        if ( w !== undefined ) {

            console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
            return this.addVectors( v, w );

        }

        this.x += v.x;
        this.y += v.y;
        this.z += v.z;

        return this;

    },

    addScalar: function ( s ) {

        this.x += s;
        this.y += s;
        this.z += s;

        return this;

    },

    addVectors: function ( a, b ) {

        this.x = a.x + b.x;
        this.y = a.y + b.y;
        this.z = a.z + b.z;

        return this;

    },

    addScaledVector: function ( v, s ) {

        this.x += v.x * s;
        this.y += v.y * s;
        this.z += v.z * s;

        return this;

    },

    sub: function ( v, w ) {

        if ( w !== undefined ) {

            console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
            return this.subVectors( v, w );

        }

        this.x -= v.x;
        this.y -= v.y;
        this.z -= v.z;

        return this;

    },

    subScalar: function ( s ) {

        this.x -= s;
        this.y -= s;
        this.z -= s;

        return this;

    },

    subVectors: function ( a, b ) {

        this.x = a.x - b.x;
        this.y = a.y - b.y;
        this.z = a.z - b.z;

        return this;

    },

    multiply: function ( v, w ) {

        if ( w !== undefined ) {

            console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
            return this.multiplyVectors( v, w );

        }

        this.x *= v.x;
        this.y *= v.y;
        this.z *= v.z;

        return this;

    },

    multiplyScalar: function ( scalar ) {

        if ( isFinite( scalar ) ) {
            this.x *= scalar;
            this.y *= scalar;
            this.z *= scalar;
        } else {
            this.x = 0;
            this.y = 0;
            this.z = 0;
        }

        return this;

    },

    multiplyVectors: function ( a, b ) {

        this.x = a.x * b.x;
        this.y = a.y * b.y;
        this.z = a.z * b.z;

        return this;

    },

    applyEuler: function () {

        var quaternion;

        return function applyEuler( euler ) {

            if ( euler instanceof THREE.Euler === false ) {

                console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' );

            }

            if ( quaternion === undefined ) quaternion = new THREE.Quaternion();

            this.applyQuaternion( quaternion.setFromEuler( euler ) );

            return this;

        };

    }(),

    applyAxisAngle: function () {

        var quaternion;

        return function applyAxisAngle( axis, angle ) {

            if ( quaternion === undefined ) quaternion = new THREE.Quaternion();

            this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );

            return this;

        };

    }(),

    applyMatrix3: function ( m ) {

        var x = this.x;
        var y = this.y;
        var z = this.z;

        var e = m.elements;

        this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
        this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
        this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;

        return this;

    },

    applyMatrix4: function ( m ) {

        // input: THREE.Matrix4 affine matrix

        var x = this.x, y = this.y, z = this.z;

        var e = m.elements;

        this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ]  * z + e[ 12 ];
        this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ]  * z + e[ 13 ];
        this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ];

        return this;

    },

    applyProjection: function ( m ) {

        // input: THREE.Matrix4 projection matrix

        var x = this.x, y = this.y, z = this.z;

        var e = m.elements;
        var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide

        this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ]  * z + e[ 12 ] ) * d;
        this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ]  * z + e[ 13 ] ) * d;
        this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d;

        return this;

    },

    applyQuaternion: function ( q ) {

        var x = this.x;
        var y = this.y;
        var z = this.z;

        var qx = q.x;
        var qy = q.y;
        var qz = q.z;
        var qw = q.w;

        // calculate quat * vector

        var ix =  qw * x + qy * z - qz * y;
        var iy =  qw * y + qz * x - qx * z;
        var iz =  qw * z + qx * y - qy * x;
        var iw = - qx * x - qy * y - qz * z;

        // calculate result * inverse quat

        this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
        this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
        this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;

        return this;

    },

    project: function () {

        var matrix;

        return function project( camera ) {

            if ( matrix === undefined ) matrix = new THREE.Matrix4();

            matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
            return this.applyProjection( matrix );

        };

    }(),

    unproject: function () {

        var matrix;

        return function unproject( camera ) {

            if ( matrix === undefined ) matrix = new THREE.Matrix4();

            matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
            return this.applyProjection( matrix );

        };

    }(),

    transformDirection: function ( m ) {

        // input: THREE.Matrix4 affine matrix
        // vector interpreted as a direction

        var x = this.x, y = this.y, z = this.z;

        var e = m.elements;

        this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ]  * z;
        this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ]  * z;
        this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;

        this.normalize();

        return this;

    },

    divide: function ( v ) {

        this.x /= v.x;
        this.y /= v.y;
        this.z /= v.z;

        return this;

    },

    divideScalar: function ( scalar ) {

        return this.multiplyScalar( 1 / scalar );

    },

    min: function ( v ) {

        this.x = Math.min( this.x, v.x );
        this.y = Math.min( this.y, v.y );
        this.z = Math.min( this.z, v.z );

        return this;

    },

    max: function ( v ) {

        this.x = Math.max( this.x, v.x );
        this.y = Math.max( this.y, v.y );
        this.z = Math.max( this.z, v.z );

        return this;

    },

    clamp: function ( min, max ) {

        // This function assumes min < max, if this assumption isn't true it will not operate correctly

        this.x = Math.max( min.x, Math.min( max.x, this.x ) );
        this.y = Math.max( min.y, Math.min( max.y, this.y ) );
        this.z = Math.max( min.z, Math.min( max.z, this.z ) );

        return this;

    },

    clampScalar: function () {

        var min, max;

        return function clampScalar( minVal, maxVal ) {

            if ( min === undefined ) {

                min = new THREE.Vector3();
                max = new THREE.Vector3();

            }

            min.set( minVal, minVal, minVal );
            max.set( maxVal, maxVal, maxVal );

            return this.clamp( min, max );

        };

    }(),

    clampLength: function ( min, max ) {

        var length = this.length();

        this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length );

        return this;

    },

    floor: function () {

        this.x = Math.floor( this.x );
        this.y = Math.floor( this.y );
        this.z = Math.floor( this.z );

        return this;

    },

    ceil: function () {

        this.x = Math.ceil( this.x );
        this.y = Math.ceil( this.y );
        this.z = Math.ceil( this.z );

        return this;

    },

    round: function () {

        this.x = Math.round( this.x );
        this.y = Math.round( this.y );
        this.z = Math.round( this.z );

        return this;

    },

    roundToZero: function () {

        this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
        this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
        this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );

        return this;

    },

    negate: function () {

        this.x = - this.x;
        this.y = - this.y;
        this.z = - this.z;

        return this;

    },

    dot: function ( v ) {

        return this.x * v.x + this.y * v.y + this.z * v.z;

    },

    lengthSq: function () {

        return this.x * this.x + this.y * this.y + this.z * this.z;

    },

    length: function () {

        return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );

    },

    lengthManhattan: function () {

        return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );

    },

    normalize: function () {

        return this.divideScalar( this.length() );

    },

    setLength: function ( length ) {

        return this.multiplyScalar( length / this.length() );

    },

    lerp: function ( v, alpha ) {

        this.x += ( v.x - this.x ) * alpha;
        this.y += ( v.y - this.y ) * alpha;
        this.z += ( v.z - this.z ) * alpha;

        return this;

    },

    lerpVectors: function ( v1, v2, alpha ) {

        this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );

        return this;

    },

    cross: function ( v, w ) {

        if ( w !== undefined ) {

            console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
            return this.crossVectors( v, w );

        }

        var x = this.x, y = this.y, z = this.z;

        this.x = y * v.z - z * v.y;
        this.y = z * v.x - x * v.z;
        this.z = x * v.y - y * v.x;

        return this;

    },

    crossVectors: function ( a, b ) {

        var ax = a.x, ay = a.y, az = a.z;
        var bx = b.x, by = b.y, bz = b.z;

        this.x = ay * bz - az * by;
        this.y = az * bx - ax * bz;
        this.z = ax * by - ay * bx;

        return this;

    },

    projectOnVector: function () {

        var v1, dot;

        return function projectOnVector( vector ) {

            if ( v1 === undefined ) v1 = new THREE.Vector3();

            v1.copy( vector ).normalize();

            dot = this.dot( v1 );

            return this.copy( v1 ).multiplyScalar( dot );

        };

    }(),

    projectOnPlane: function () {

        var v1;

        return function projectOnPlane( planeNormal ) {

            if ( v1 === undefined ) v1 = new THREE.Vector3();

            v1.copy( this ).projectOnVector( planeNormal );

            return this.sub( v1 );

        }

    }(),

    reflect: function () {

        // reflect incident vector off plane orthogonal to normal
        // normal is assumed to have unit length

        var v1;

        return function reflect( normal ) {

            if ( v1 === undefined ) v1 = new THREE.Vector3();

            return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );

        }

    }(),

    angleTo: function ( v ) {

        var theta = this.dot( v ) / ( this.length() * v.length() );

        // clamp, to handle numerical problems

        return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) );

    },

    distanceTo: function ( v ) {

        return Math.sqrt( this.distanceToSquared( v ) );

    },

    distanceToSquared: function ( v ) {

        var dx = this.x - v.x;
        var dy = this.y - v.y;
        var dz = this.z - v.z;

        return dx * dx + dy * dy + dz * dz;

    },

    setEulerFromRotationMatrix: function ( m, order ) {

        console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );

    },

    setEulerFromQuaternion: function ( q, order ) {

        console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );

    },

    getPositionFromMatrix: function ( m ) {

        console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );

        return this.setFromMatrixPosition( m );

    },

    getScaleFromMatrix: function ( m ) {

        console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );

        return this.setFromMatrixScale( m );

    },

    getColumnFromMatrix: function ( index, matrix ) {

        console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );

        return this.setFromMatrixColumn( index, matrix );

    },

    setFromMatrixPosition: function ( m ) {

        this.x = m.elements[ 12 ];
        this.y = m.elements[ 13 ];
        this.z = m.elements[ 14 ];

        return this;

    },

    setFromMatrixScale: function ( m ) {

        var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length();
        var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length();
        var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length();

        this.x = sx;
        this.y = sy;
        this.z = sz;

        return this;

    },

    setFromMatrixColumn: function ( index, matrix ) {

        var offset = index * 4;

        var me = matrix.elements;

        this.x = me[ offset ];
        this.y = me[ offset + 1 ];
        this.z = me[ offset + 2 ];

        return this;

    },

    equals: function ( v ) {

        return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );

    },

    fromArray: function ( array, offset ) {

        if ( offset === undefined ) offset = 0;

        this.x = array[ offset ];
        this.y = array[ offset + 1 ];
        this.z = array[ offset + 2 ];

        return this;

    },

    toArray: function ( array, offset ) {

        if ( array === undefined ) array = [];
        if ( offset === undefined ) offset = 0;

        array[ offset ] = this.x;
        array[ offset + 1 ] = this.y;
        array[ offset + 2 ] = this.z;

        return array;

    },

    fromAttribute: function ( attribute, index, offset ) {

        if ( offset === undefined ) offset = 0;

        index = index * attribute.itemSize + offset;

        this.x = attribute.array[ index ];
        this.y = attribute.array[ index + 1 ];
        this.z = attribute.array[ index + 2 ];

        return this;

    }

};

// File:src/math/Vector4.js

/**
 * @author supereggbert / http://www.paulbrunt.co.uk/
 * @author philogb / http://blog.thejit.org/
 * @author mikael emtinger / http://gomo.se/
 * @author egraether / http://egraether.com/
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.Vector4 = function ( x, y, z, w ) {

    this.x = x || 0;
    this.y = y || 0;
    this.z = z || 0;
    this.w = ( w !== undefined ) ? w : 1;

};

THREE.Vector4.prototype = {

    constructor: THREE.Vector4,

    set: function ( x, y, z, w ) {

        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;

        return this;

    },

    setX: function ( x ) {

        this.x = x;

        return this;

    },

    setY: function ( y ) {

        this.y = y;

        return this;

    },

    setZ: function ( z ) {

        this.z = z;

        return this;

    },

    setW: function ( w ) {

        this.w = w;

        return this;

    },

    setComponent: function ( index, value ) {

        switch ( index ) {

            case 0: this.x = value; break;
            case 1: this.y = value; break;
            case 2: this.z = value; break;
            case 3: this.w = value; break;
            default: throw new Error( 'index is out of range: ' + index );

        }

    },

    getComponent: function ( index ) {

        switch ( index ) {

            case 0: return this.x;
            case 1: return this.y;
            case 2: return this.z;
            case 3: return this.w;
            default: throw new Error( 'index is out of range: ' + index );

        }

    },

    clone: function () {

        return new this.constructor( this.x, this.y, this.z, this.w );

    },

    copy: function ( v ) {

        this.x = v.x;
        this.y = v.y;
        this.z = v.z;
        this.w = ( v.w !== undefined ) ? v.w : 1;

        return this;

    },

    add: function ( v, w ) {

        if ( w !== undefined ) {

            console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
            return this.addVectors( v, w );

        }

        this.x += v.x;
        this.y += v.y;
        this.z += v.z;
        this.w += v.w;

        return this;

    },

    addScalar: function ( s ) {

        this.x += s;
        this.y += s;
        this.z += s;
        this.w += s;

        return this;

    },

    addVectors: function ( a, b ) {

        this.x = a.x + b.x;
        this.y = a.y + b.y;
        this.z = a.z + b.z;
        this.w = a.w + b.w;

        return this;

    },

    addScaledVector: function ( v, s ) {

        this.x += v.x * s;
        this.y += v.y * s;
        this.z += v.z * s;
        this.w += v.w * s;

        return this;

    },

    sub: function ( v, w ) {

        if ( w !== undefined ) {

            console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
            return this.subVectors( v, w );

        }

        this.x -= v.x;
        this.y -= v.y;
        this.z -= v.z;
        this.w -= v.w;

        return this;

    },

    subScalar: function ( s ) {

        this.x -= s;
        this.y -= s;
        this.z -= s;
        this.w -= s;

        return this;

    },

    subVectors: function ( a, b ) {

        this.x = a.x - b.x;
        this.y = a.y - b.y;
        this.z = a.z - b.z;
        this.w = a.w - b.w;

        return this;

    },

    multiplyScalar: function ( scalar ) {

        if ( isFinite( scalar ) ) {
            this.x *= scalar;
            this.y *= scalar;
            this.z *= scalar;
            this.w *= scalar;
        } else {
            this.x = 0;
            this.y = 0;
            this.z = 0;
            this.w = 0;
        }

        return this;

    },

    applyMatrix4: function ( m ) {

        var x = this.x;
        var y = this.y;
        var z = this.z;
        var w = this.w;

        var e = m.elements;

        this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;
        this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;
        this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;
        this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;

        return this;

    },

    divideScalar: function ( scalar ) {

        return this.multiplyScalar( 1 / scalar );

    },

    setAxisAngleFromQuaternion: function ( q ) {

        // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm

        // q is assumed to be normalized

        this.w = 2 * Math.acos( q.w );

        var s = Math.sqrt( 1 - q.w * q.w );

        if ( s < 0.0001 ) {

            this.x = 1;
            this.y = 0;
            this.z = 0;

        } else {

            this.x = q.x / s;
            this.y = q.y / s;
            this.z = q.z / s;

        }

        return this;

    },

    setAxisAngleFromRotationMatrix: function ( m ) {

        // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm

        // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)

        var angle, x, y, z,		// variables for result
            epsilon = 0.01,		// margin to allow for rounding errors
            epsilon2 = 0.1,		// margin to distinguish between 0 and 180 degrees

            te = m.elements,

            m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
            m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
            m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];

        if ( ( Math.abs( m12 - m21 ) < epsilon )
            && ( Math.abs( m13 - m31 ) < epsilon )
            && ( Math.abs( m23 - m32 ) < epsilon ) ) {

            // singularity found
            // first check for identity matrix which must have +1 for all terms
            // in leading diagonal and zero in other terms

            if ( ( Math.abs( m12 + m21 ) < epsilon2 )
                && ( Math.abs( m13 + m31 ) < epsilon2 )
                && ( Math.abs( m23 + m32 ) < epsilon2 )
                && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {

                // this singularity is identity matrix so angle = 0

                this.set( 1, 0, 0, 0 );

                return this; // zero angle, arbitrary axis

            }

            // otherwise this singularity is angle = 180

            angle = Math.PI;

            var xx = ( m11 + 1 ) / 2;
            var yy = ( m22 + 1 ) / 2;
            var zz = ( m33 + 1 ) / 2;
            var xy = ( m12 + m21 ) / 4;
            var xz = ( m13 + m31 ) / 4;
            var yz = ( m23 + m32 ) / 4;

            if ( ( xx > yy ) && ( xx > zz ) ) {

                // m11 is the largest diagonal term

                if ( xx < epsilon ) {

                    x = 0;
                    y = 0.707106781;
                    z = 0.707106781;

                } else {

                    x = Math.sqrt( xx );
                    y = xy / x;
                    z = xz / x;

                }

            } else if ( yy > zz ) {

                // m22 is the largest diagonal term

                if ( yy < epsilon ) {

                    x = 0.707106781;
                    y = 0;
                    z = 0.707106781;

                } else {

                    y = Math.sqrt( yy );
                    x = xy / y;
                    z = yz / y;

                }

            } else {

                // m33 is the largest diagonal term so base result on this

                if ( zz < epsilon ) {

                    x = 0.707106781;
                    y = 0.707106781;
                    z = 0;

                } else {

                    z = Math.sqrt( zz );
                    x = xz / z;
                    y = yz / z;

                }

            }

            this.set( x, y, z, angle );

            return this; // return 180 deg rotation

        }

        // as we have reached here there are no singularities so we can handle normally

        var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 )
            + ( m13 - m31 ) * ( m13 - m31 )
            + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize

        if ( Math.abs( s ) < 0.001 ) s = 1;

        // prevent divide by zero, should not happen if matrix is orthogonal and should be
        // caught by singularity test above, but I've left it in just in case

        this.x = ( m32 - m23 ) / s;
        this.y = ( m13 - m31 ) / s;
        this.z = ( m21 - m12 ) / s;
        this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );

        return this;

    },

    min: function ( v ) {

        this.x = Math.min( this.x, v.x );
        this.y = Math.min( this.y, v.y );
        this.z = Math.min( this.z, v.z );
        this.w = Math.min( this.w, v.w );

        return this;

    },

    max: function ( v ) {

        this.x = Math.max( this.x, v.x );
        this.y = Math.max( this.y, v.y );
        this.z = Math.max( this.z, v.z );
        this.w = Math.max( this.w, v.w );

        return this;

    },

    clamp: function ( min, max ) {

        // This function assumes min < max, if this assumption isn't true it will not operate correctly

        this.x = Math.max( min.x, Math.min( max.x, this.x ) );
        this.y = Math.max( min.y, Math.min( max.y, this.y ) );
        this.z = Math.max( min.z, Math.min( max.z, this.z ) );
        this.w = Math.max( min.w, Math.min( max.w, this.w ) );

        return this;

    },

    clampScalar: function () {

        var min, max;

        return function clampScalar( minVal, maxVal ) {

            if ( min === undefined ) {

                min = new THREE.Vector4();
                max = new THREE.Vector4();

            }

            min.set( minVal, minVal, minVal, minVal );
            max.set( maxVal, maxVal, maxVal, maxVal );

            return this.clamp( min, max );

        };

    }(),

    floor: function () {

        this.x = Math.floor( this.x );
        this.y = Math.floor( this.y );
        this.z = Math.floor( this.z );
        this.w = Math.floor( this.w );

        return this;

    },

    ceil: function () {

        this.x = Math.ceil( this.x );
        this.y = Math.ceil( this.y );
        this.z = Math.ceil( this.z );
        this.w = Math.ceil( this.w );

        return this;

    },

    round: function () {

        this.x = Math.round( this.x );
        this.y = Math.round( this.y );
        this.z = Math.round( this.z );
        this.w = Math.round( this.w );

        return this;

    },

    roundToZero: function () {

        this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
        this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
        this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
        this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );

        return this;

    },

    negate: function () {

        this.x = - this.x;
        this.y = - this.y;
        this.z = - this.z;
        this.w = - this.w;

        return this;

    },

    dot: function ( v ) {

        return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;

    },

    lengthSq: function () {

        return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;

    },

    length: function () {

        return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );

    },

    lengthManhattan: function () {

        return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );

    },

    normalize: function () {

        return this.divideScalar( this.length() );

    },

    setLength: function ( length ) {

        return this.multiplyScalar( length / this.length() );

    },

    lerp: function ( v, alpha ) {

        this.x += ( v.x - this.x ) * alpha;
        this.y += ( v.y - this.y ) * alpha;
        this.z += ( v.z - this.z ) * alpha;
        this.w += ( v.w - this.w ) * alpha;

        return this;

    },

    lerpVectors: function ( v1, v2, alpha ) {

        this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );

        return this;

    },

    equals: function ( v ) {

        return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );

    },

    fromArray: function ( array, offset ) {

        if ( offset === undefined ) offset = 0;

        this.x = array[ offset ];
        this.y = array[ offset + 1 ];
        this.z = array[ offset + 2 ];
        this.w = array[ offset + 3 ];

        return this;

    },

    toArray: function ( array, offset ) {

        if ( array === undefined ) array = [];
        if ( offset === undefined ) offset = 0;

        array[ offset ] = this.x;
        array[ offset + 1 ] = this.y;
        array[ offset + 2 ] = this.z;
        array[ offset + 3 ] = this.w;

        return array;

    },

    fromAttribute: function ( attribute, index, offset ) {

        if ( offset === undefined ) offset = 0;

        index = index * attribute.itemSize + offset;

        this.x = attribute.array[ index ];
        this.y = attribute.array[ index + 1 ];
        this.z = attribute.array[ index + 2 ];
        this.w = attribute.array[ index + 3 ];

        return this;

    }

};

// File:src/math/Euler.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author WestLangley / http://github.com/WestLangley
 * @author bhouston / http://clara.io
 */

THREE.Euler = function ( x, y, z, order ) {

    this._x = x || 0;
    this._y = y || 0;
    this._z = z || 0;
    this._order = order || THREE.Euler.DefaultOrder;

};

THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];

THREE.Euler.DefaultOrder = 'XYZ';

THREE.Euler.prototype = {

    constructor: THREE.Euler,

    get x () {

        return this._x;

    },

    set x ( value ) {

        this._x = value;
        this.onChangeCallback();

    },

    get y () {

        return this._y;

    },

    set y ( value ) {

        this._y = value;
        this.onChangeCallback();

    },

    get z () {

        return this._z;

    },

    set z ( value ) {

        this._z = value;
        this.onChangeCallback();

    },

    get order () {

        return this._order;

    },

    set order ( value ) {

        this._order = value;
        this.onChangeCallback();

    },

    set: function ( x, y, z, order ) {

        this._x = x;
        this._y = y;
        this._z = z;
        this._order = order || this._order;

        this.onChangeCallback();

        return this;

    },

    clone: function () {

        return new this.constructor( this._x, this._y, this._z, this._order);

    },

    copy: function ( euler ) {

        this._x = euler._x;
        this._y = euler._y;
        this._z = euler._z;
        this._order = euler._order;

        this.onChangeCallback();

        return this;

    },

    setFromRotationMatrix: function ( m, order, update ) {

        var clamp = THREE.Math.clamp;

        // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)

        var te = m.elements;
        var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
        var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
        var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];

        order = order || this._order;

        if ( order === 'XYZ' ) {

            this._y = Math.asin( clamp( m13, - 1, 1 ) );

            if ( Math.abs( m13 ) < 0.99999 ) {

                this._x = Math.atan2( - m23, m33 );
                this._z = Math.atan2( - m12, m11 );

            } else {

                this._x = Math.atan2( m32, m22 );
                this._z = 0;

            }

        } else if ( order === 'YXZ' ) {

            this._x = Math.asin( - clamp( m23, - 1, 1 ) );

            if ( Math.abs( m23 ) < 0.99999 ) {

                this._y = Math.atan2( m13, m33 );
                this._z = Math.atan2( m21, m22 );

            } else {

                this._y = Math.atan2( - m31, m11 );
                this._z = 0;

            }

        } else if ( order === 'ZXY' ) {

            this._x = Math.asin( clamp( m32, - 1, 1 ) );

            if ( Math.abs( m32 ) < 0.99999 ) {

                this._y = Math.atan2( - m31, m33 );
                this._z = Math.atan2( - m12, m22 );

            } else {

                this._y = 0;
                this._z = Math.atan2( m21, m11 );

            }

        } else if ( order === 'ZYX' ) {

            this._y = Math.asin( - clamp( m31, - 1, 1 ) );

            if ( Math.abs( m31 ) < 0.99999 ) {

                this._x = Math.atan2( m32, m33 );
                this._z = Math.atan2( m21, m11 );

            } else {

                this._x = 0;
                this._z = Math.atan2( - m12, m22 );

            }

        } else if ( order === 'YZX' ) {

            this._z = Math.asin( clamp( m21, - 1, 1 ) );

            if ( Math.abs( m21 ) < 0.99999 ) {

                this._x = Math.atan2( - m23, m22 );
                this._y = Math.atan2( - m31, m11 );

            } else {

                this._x = 0;
                this._y = Math.atan2( m13, m33 );

            }

        } else if ( order === 'XZY' ) {

            this._z = Math.asin( - clamp( m12, - 1, 1 ) );

            if ( Math.abs( m12 ) < 0.99999 ) {

                this._x = Math.atan2( m32, m22 );
                this._y = Math.atan2( m13, m11 );

            } else {

                this._x = Math.atan2( - m23, m33 );
                this._y = 0;

            }

        } else {

            console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order )

        }

        this._order = order;

        if ( update !== false ) this.onChangeCallback();

        return this;

    },

    setFromQuaternion: function () {

        var matrix;

        return function ( q, order, update ) {

            if ( matrix === undefined ) matrix = new THREE.Matrix4();
            matrix.makeRotationFromQuaternion( q );
            this.setFromRotationMatrix( matrix, order, update );

            return this;

        };

    }(),

    setFromVector3: function ( v, order ) {

        return this.set( v.x, v.y, v.z, order || this._order );

    },

    reorder: function () {

        // WARNING: this discards revolution information -bhouston

        var q = new THREE.Quaternion();

        return function ( newOrder ) {

            q.setFromEuler( this );
            this.setFromQuaternion( q, newOrder );

        };

    }(),

    equals: function ( euler ) {

        return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );

    },

    fromArray: function ( array ) {

        this._x = array[ 0 ];
        this._y = array[ 1 ];
        this._z = array[ 2 ];
        if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];

        this.onChangeCallback();

        return this;

    },

    toArray: function ( array, offset ) {

        if ( array === undefined ) array = [];
        if ( offset === undefined ) offset = 0;

        array[ offset ] = this._x;
        array[ offset + 1 ] = this._y;
        array[ offset + 2 ] = this._z;
        array[ offset + 3 ] = this._order;

        return array;

    },

    toVector3: function ( optionalResult ) {

        if ( optionalResult ) {

            return optionalResult.set( this._x, this._y, this._z );

        } else {

            return new THREE.Vector3( this._x, this._y, this._z );

        }

    },

    onChange: function ( callback ) {

        this.onChangeCallback = callback;

        return this;

    },

    onChangeCallback: function () {}

};

// File:src/math/Line3.js

/**
 * @author bhouston / http://clara.io
 */

THREE.Line3 = function ( start, end ) {

    this.start = ( start !== undefined ) ? start : new THREE.Vector3();
    this.end = ( end !== undefined ) ? end : new THREE.Vector3();

};

THREE.Line3.prototype = {

    constructor: THREE.Line3,

    set: function ( start, end ) {

        this.start.copy( start );
        this.end.copy( end );

        return this;

    },

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( line ) {

        this.start.copy( line.start );
        this.end.copy( line.end );

        return this;

    },

    center: function ( optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();
        return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 );

    },

    delta: function ( optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();
        return result.subVectors( this.end, this.start );

    },

    distanceSq: function () {

        return this.start.distanceToSquared( this.end );

    },

    distance: function () {

        return this.start.distanceTo( this.end );

    },

    at: function ( t, optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();

        return this.delta( result ).multiplyScalar( t ).add( this.start );

    },

    closestPointToPointParameter: function () {

        var startP = new THREE.Vector3();
        var startEnd = new THREE.Vector3();

        return function ( point, clampToLine ) {

            startP.subVectors( point, this.start );
            startEnd.subVectors( this.end, this.start );

            var startEnd2 = startEnd.dot( startEnd );
            var startEnd_startP = startEnd.dot( startP );

            var t = startEnd_startP / startEnd2;

            if ( clampToLine ) {

                t = THREE.Math.clamp( t, 0, 1 );

            }

            return t;

        };

    }(),

    closestPointToPoint: function ( point, clampToLine, optionalTarget ) {

        var t = this.closestPointToPointParameter( point, clampToLine );

        var result = optionalTarget || new THREE.Vector3();

        return this.delta( result ).multiplyScalar( t ).add( this.start );

    },

    applyMatrix4: function ( matrix ) {

        this.start.applyMatrix4( matrix );
        this.end.applyMatrix4( matrix );

        return this;

    },

    equals: function ( line ) {

        return line.start.equals( this.start ) && line.end.equals( this.end );

    }

};

// File:src/math/Box2.js

/**
 * @author bhouston / http://clara.io
 */

THREE.Box2 = function ( min, max ) {

    this.min = ( min !== undefined ) ? min : new THREE.Vector2( Infinity, Infinity );
    this.max = ( max !== undefined ) ? max : new THREE.Vector2( - Infinity, - Infinity );

};

THREE.Box2.prototype = {

    constructor: THREE.Box2,

    set: function ( min, max ) {

        this.min.copy( min );
        this.max.copy( max );

        return this;

    },

    setFromPoints: function ( points ) {

        this.makeEmpty();

        for ( var i = 0, il = points.length; i < il; i ++ ) {

            this.expandByPoint( points[ i ] )

        }

        return this;

    },

    setFromCenterAndSize: function () {

        var v1 = new THREE.Vector2();

        return function ( center, size ) {

            var halfSize = v1.copy( size ).multiplyScalar( 0.5 );
            this.min.copy( center ).sub( halfSize );
            this.max.copy( center ).add( halfSize );

            return this;

        };

    }(),

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( box ) {

        this.min.copy( box.min );
        this.max.copy( box.max );

        return this;

    },

    makeEmpty: function () {

        this.min.x = this.min.y = Infinity;
        this.max.x = this.max.y = - Infinity;

        return this;

    },

    empty: function () {

        // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes

        return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );

    },

    center: function ( optionalTarget ) {

        var result = optionalTarget || new THREE.Vector2();
        return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );

    },

    size: function ( optionalTarget ) {

        var result = optionalTarget || new THREE.Vector2();
        return result.subVectors( this.max, this.min );

    },

    expandByPoint: function ( point ) {

        this.min.min( point );
        this.max.max( point );

        return this;

    },

    expandByVector: function ( vector ) {

        this.min.sub( vector );
        this.max.add( vector );

        return this;

    },

    expandByScalar: function ( scalar ) {

        this.min.addScalar( - scalar );
        this.max.addScalar( scalar );

        return this;

    },

    containsPoint: function ( point ) {

        if ( point.x < this.min.x || point.x > this.max.x ||
            point.y < this.min.y || point.y > this.max.y ) {

            return false;

        }

        return true;

    },

    containsBox: function ( box ) {

        if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
            ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) {

            return true;

        }

        return false;

    },

    getParameter: function ( point, optionalTarget ) {

        // This can potentially have a divide by zero if the box
        // has a size dimension of 0.

        var result = optionalTarget || new THREE.Vector2();

        return result.set(
            ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
            ( point.y - this.min.y ) / ( this.max.y - this.min.y )
        );

    },

    isIntersectionBox: function ( box ) {

        // using 6 splitting planes to rule out intersections.

        if ( box.max.x < this.min.x || box.min.x > this.max.x ||
            box.max.y < this.min.y || box.min.y > this.max.y ) {

            return false;

        }

        return true;

    },

    clampPoint: function ( point, optionalTarget ) {

        var result = optionalTarget || new THREE.Vector2();
        return result.copy( point ).clamp( this.min, this.max );

    },

    distanceToPoint: function () {

        var v1 = new THREE.Vector2();

        return function ( point ) {

            var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
            return clampedPoint.sub( point ).length();

        };

    }(),

    intersect: function ( box ) {

        this.min.max( box.min );
        this.max.min( box.max );

        return this;

    },

    union: function ( box ) {

        this.min.min( box.min );
        this.max.max( box.max );

        return this;

    },

    translate: function ( offset ) {

        this.min.add( offset );
        this.max.add( offset );

        return this;

    },

    equals: function ( box ) {

        return box.min.equals( this.min ) && box.max.equals( this.max );

    }

};

// File:src/math/Box3.js

/**
 * @author bhouston / http://clara.io
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.Box3 = function ( min, max ) {

    this.min = ( min !== undefined ) ? min : new THREE.Vector3( Infinity, Infinity, Infinity );
    this.max = ( max !== undefined ) ? max : new THREE.Vector3( - Infinity, - Infinity, - Infinity );

};

THREE.Box3.prototype = {

    constructor: THREE.Box3,

    set: function ( min, max ) {

        this.min.copy( min );
        this.max.copy( max );

        return this;

    },

    setFromPoints: function ( points ) {

        this.makeEmpty();

        for ( var i = 0, il = points.length; i < il; i ++ ) {

            this.expandByPoint( points[ i ] );

        }

        return this;

    },

    setFromCenterAndSize: function () {

        var v1 = new THREE.Vector3();

        return function ( center, size ) {

            var halfSize = v1.copy( size ).multiplyScalar( 0.5 );

            this.min.copy( center ).sub( halfSize );
            this.max.copy( center ).add( halfSize );

            return this;

        };

    }(),

    setFromObject: function () {

        // Computes the world-axis-aligned bounding box of an object (including its children),
        // accounting for both the object's, and children's, world transforms

        var v1 = new THREE.Vector3();

        return function ( object ) {

            var scope = this;

            object.updateMatrixWorld( true );

            this.makeEmpty();

            object.traverse( function ( node ) {

                var geometry = node.geometry;

                if ( geometry !== undefined ) {

                    if ( geometry instanceof THREE.Geometry ) {

                        var vertices = geometry.vertices;

                        for ( var i = 0, il = vertices.length; i < il; i ++ ) {

                            v1.copy( vertices[ i ] );

                            v1.applyMatrix4( node.matrixWorld );

                            scope.expandByPoint( v1 );

                        }

                    } else if ( geometry instanceof THREE.BufferGeometry && geometry.attributes[ 'position' ] !== undefined ) {

                        var positions = geometry.attributes[ 'position' ].array;

                        for ( var i = 0, il = positions.length; i < il; i += 3 ) {

                            v1.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] );

                            v1.applyMatrix4( node.matrixWorld );

                            scope.expandByPoint( v1 );

                        }

                    }

                }

            } );

            return this;

        };

    }(),

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( box ) {

        this.min.copy( box.min );
        this.max.copy( box.max );

        return this;

    },

    makeEmpty: function () {

        this.min.x = this.min.y = this.min.z = Infinity;
        this.max.x = this.max.y = this.max.z = - Infinity;

        return this;

    },

    empty: function () {

        // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes

        return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );

    },

    center: function ( optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();
        return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 );

    },

    size: function ( optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();
        return result.subVectors( this.max, this.min );

    },

    expandByPoint: function ( point ) {

        this.min.min( point );
        this.max.max( point );

        return this;

    },

    expandByVector: function ( vector ) {

        this.min.sub( vector );
        this.max.add( vector );

        return this;

    },

    expandByScalar: function ( scalar ) {

        this.min.addScalar( - scalar );
        this.max.addScalar( scalar );

        return this;

    },

    containsPoint: function ( point ) {

        if ( point.x < this.min.x || point.x > this.max.x ||
            point.y < this.min.y || point.y > this.max.y ||
            point.z < this.min.z || point.z > this.max.z ) {

            return false;

        }

        return true;

    },

    containsBox: function ( box ) {

        if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) &&
            ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) &&
            ( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) {

            return true;

        }

        return false;

    },

    getParameter: function ( point, optionalTarget ) {

        // This can potentially have a divide by zero if the box
        // has a size dimension of 0.

        var result = optionalTarget || new THREE.Vector3();

        return result.set(
            ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
            ( point.y - this.min.y ) / ( this.max.y - this.min.y ),
            ( point.z - this.min.z ) / ( this.max.z - this.min.z )
        );

    },

    isIntersectionBox: function ( box ) {

        // using 6 splitting planes to rule out intersections.

        if ( box.max.x < this.min.x || box.min.x > this.max.x ||
            box.max.y < this.min.y || box.min.y > this.max.y ||
            box.max.z < this.min.z || box.min.z > this.max.z ) {

            return false;

        }

        return true;

    },

    clampPoint: function ( point, optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();
        return result.copy( point ).clamp( this.min, this.max );

    },

    distanceToPoint: function () {

        var v1 = new THREE.Vector3();

        return function ( point ) {

            var clampedPoint = v1.copy( point ).clamp( this.min, this.max );
            return clampedPoint.sub( point ).length();

        };

    }(),

    getBoundingSphere: function () {

        var v1 = new THREE.Vector3();

        return function ( optionalTarget ) {

            var result = optionalTarget || new THREE.Sphere();

            result.center = this.center();
            result.radius = this.size( v1 ).length() * 0.5;

            return result;

        };

    }(),

    intersect: function ( box ) {

        this.min.max( box.min );
        this.max.min( box.max );

        return this;

    },

    union: function ( box ) {

        this.min.min( box.min );
        this.max.max( box.max );

        return this;

    },

    applyMatrix4: function () {

        var points = [
            new THREE.Vector3(),
            new THREE.Vector3(),
            new THREE.Vector3(),
            new THREE.Vector3(),
            new THREE.Vector3(),
            new THREE.Vector3(),
            new THREE.Vector3(),
            new THREE.Vector3()
        ];

        return function ( matrix ) {

            // NOTE: I am using a binary pattern to specify all 2^3 combinations below
            points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
            points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
            points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
            points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
            points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
            points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
            points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
            points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix );  // 111

            this.makeEmpty();
            this.setFromPoints( points );

            return this;

        };

    }(),

    translate: function ( offset ) {

        this.min.add( offset );
        this.max.add( offset );

        return this;

    },

    equals: function ( box ) {

        return box.min.equals( this.min ) && box.max.equals( this.max );

    }

};

// File:src/math/Matrix3.js

/**
 * @author alteredq / http://alteredqualia.com/
 * @author WestLangley / http://github.com/WestLangley
 * @author bhouston / http://clara.io
 */

THREE.Matrix3 = function () {

    this.elements = new Float32Array( [

        1, 0, 0,
        0, 1, 0,
        0, 0, 1

    ] );

    if ( arguments.length > 0 ) {

        console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );

    }

};

THREE.Matrix3.prototype = {

    constructor: THREE.Matrix3,

    set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {

        var te = this.elements;

        te[ 0 ] = n11; te[ 3 ] = n12; te[ 6 ] = n13;
        te[ 1 ] = n21; te[ 4 ] = n22; te[ 7 ] = n23;
        te[ 2 ] = n31; te[ 5 ] = n32; te[ 8 ] = n33;

        return this;

    },

    identity: function () {

        this.set(

            1, 0, 0,
            0, 1, 0,
            0, 0, 1

        );

        return this;

    },

    clone: function () {

        return new this.constructor().fromArray( this.elements );

    },

    copy: function ( m ) {

        var me = m.elements;

        this.set(

            me[ 0 ], me[ 3 ], me[ 6 ],
            me[ 1 ], me[ 4 ], me[ 7 ],
            me[ 2 ], me[ 5 ], me[ 8 ]

        );

        return this;

    },

    multiplyVector3: function ( vector ) {

        console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );
        return vector.applyMatrix3( this );

    },

    multiplyVector3Array: function ( a ) {

        console.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' );
        return this.applyToVector3Array( a );

    },

    applyToVector3Array: function () {

        var v1;

        return function ( array, offset, length ) {

            if ( v1 === undefined ) v1 = new THREE.Vector3();
            if ( offset === undefined ) offset = 0;
            if ( length === undefined ) length = array.length;

            for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {

                v1.fromArray( array, j );
                v1.applyMatrix3( this );
                v1.toArray( array, j );

            }

            return array;

        };

    }(),

    applyToBuffer: function () {

        var v1;

        return function applyToBuffer( buffer, offset, length ) {

            if ( v1 === undefined ) v1 = new THREE.Vector3();
            if ( offset === undefined ) offset = 0;
            if ( length === undefined ) length = buffer.length / buffer.itemSize;

            for ( var i = 0, j = offset; i < length; i ++, j ++ ) {

                v1.x = buffer.getX( j );
                v1.y = buffer.getY( j );
                v1.z = buffer.getZ( j );

                v1.applyMatrix3( this );

                buffer.setXYZ( v1.x, v1.y, v1.z );

            }

            return buffer;

        };

    }(),

    multiplyScalar: function ( s ) {

        var te = this.elements;

        te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
        te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
        te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;

        return this;

    },

    determinant: function () {

        var te = this.elements;

        var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
            d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
            g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];

        return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;

    },

    getInverse: function ( matrix, throwOnInvertible ) {

        // input: THREE.Matrix4
        // ( based on http://code.google.com/p/webgl-mjs/ )

        var me = matrix.elements;
        var te = this.elements;

        te[ 0 ] =   me[ 10 ] * me[ 5 ] - me[ 6 ] * me[ 9 ];
        te[ 1 ] = - me[ 10 ] * me[ 1 ] + me[ 2 ] * me[ 9 ];
        te[ 2 ] =   me[ 6 ] * me[ 1 ] - me[ 2 ] * me[ 5 ];
        te[ 3 ] = - me[ 10 ] * me[ 4 ] + me[ 6 ] * me[ 8 ];
        te[ 4 ] =   me[ 10 ] * me[ 0 ] - me[ 2 ] * me[ 8 ];
        te[ 5 ] = - me[ 6 ] * me[ 0 ] + me[ 2 ] * me[ 4 ];
        te[ 6 ] =   me[ 9 ] * me[ 4 ] - me[ 5 ] * me[ 8 ];
        te[ 7 ] = - me[ 9 ] * me[ 0 ] + me[ 1 ] * me[ 8 ];
        te[ 8 ] =   me[ 5 ] * me[ 0 ] - me[ 1 ] * me[ 4 ];

        var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ];

        // no inverse

        if ( det === 0 ) {

            var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0";

            if ( throwOnInvertible || false ) {

                throw new Error( msg );

            } else {

                console.warn( msg );

            }

            this.identity();

            return this;

        }

        this.multiplyScalar( 1.0 / det );

        return this;

    },

    transpose: function () {

        var tmp, m = this.elements;

        tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
        tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
        tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;

        return this;

    },

    flattenToArrayOffset: function ( array, offset ) {

        var te = this.elements;

        array[ offset ] = te[ 0 ];
        array[ offset + 1 ] = te[ 1 ];
        array[ offset + 2 ] = te[ 2 ];

        array[ offset + 3 ] = te[ 3 ];
        array[ offset + 4 ] = te[ 4 ];
        array[ offset + 5 ] = te[ 5 ];

        array[ offset + 6 ] = te[ 6 ];
        array[ offset + 7 ] = te[ 7 ];
        array[ offset + 8 ]  = te[ 8 ];

        return array;

    },

    getNormalMatrix: function ( m ) {

        // input: THREE.Matrix4

        this.getInverse( m ).transpose();

        return this;

    },

    transposeIntoArray: function ( r ) {

        var m = this.elements;

        r[ 0 ] = m[ 0 ];
        r[ 1 ] = m[ 3 ];
        r[ 2 ] = m[ 6 ];
        r[ 3 ] = m[ 1 ];
        r[ 4 ] = m[ 4 ];
        r[ 5 ] = m[ 7 ];
        r[ 6 ] = m[ 2 ];
        r[ 7 ] = m[ 5 ];
        r[ 8 ] = m[ 8 ];

        return this;

    },

    fromArray: function ( array ) {

        this.elements.set( array );

        return this;

    },

    toArray: function () {

        var te = this.elements;

        return [
            te[ 0 ], te[ 1 ], te[ 2 ],
            te[ 3 ], te[ 4 ], te[ 5 ],
            te[ 6 ], te[ 7 ], te[ 8 ]
        ];

    }

};

// File:src/math/Matrix4.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author supereggbert / http://www.paulbrunt.co.uk/
 * @author philogb / http://blog.thejit.org/
 * @author jordi_ros / http://plattsoft.com
 * @author D1plo1d / http://github.com/D1plo1d
 * @author alteredq / http://alteredqualia.com/
 * @author mikael emtinger / http://gomo.se/
 * @author timknip / http://www.floorplanner.com/
 * @author bhouston / http://clara.io
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.Matrix4 = function () {

    this.elements = new Float32Array( [

        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1

    ] );

    if ( arguments.length > 0 ) {

        console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );

    }

};

THREE.Matrix4.prototype = {

    constructor: THREE.Matrix4,

    set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {

        var te = this.elements;

        te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
        te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
        te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
        te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;

        return this;

    },

    identity: function () {

        this.set(

            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1

        );

        return this;

    },

    clone: function () {

        return new THREE.Matrix4().fromArray( this.elements );

    },

    copy: function ( m ) {

        this.elements.set( m.elements );

        return this;

    },

    extractPosition: function ( m ) {

        console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' );
        return this.copyPosition( m );

    },

    copyPosition: function ( m ) {

        var te = this.elements;
        var me = m.elements;

        te[ 12 ] = me[ 12 ];
        te[ 13 ] = me[ 13 ];
        te[ 14 ] = me[ 14 ];

        return this;

    },

    extractBasis: function ( xAxis, yAxis, zAxis ) {

        var te = this.elements;

        xAxis.set( te[ 0 ], te[ 1 ], te[ 2 ] );
        yAxis.set( te[ 4 ], te[ 5 ], te[ 6 ] );
        zAxis.set( te[ 8 ], te[ 9 ], te[ 10 ] );

        return this;

    },

    makeBasis: function ( xAxis, yAxis, zAxis ) {

        this.set(
            xAxis.x, yAxis.x, zAxis.x, 0,
            xAxis.y, yAxis.y, zAxis.y, 0,
            xAxis.z, yAxis.z, zAxis.z, 0,
            0,       0,       0,       1
        );

        return this;

    },

    extractRotation: function () {

        var v1;

        return function ( m ) {

            if ( v1 === undefined ) v1 = new THREE.Vector3();

            var te = this.elements;
            var me = m.elements;

            var scaleX = 1 / v1.set( me[ 0 ], me[ 1 ], me[ 2 ] ).length();
            var scaleY = 1 / v1.set( me[ 4 ], me[ 5 ], me[ 6 ] ).length();
            var scaleZ = 1 / v1.set( me[ 8 ], me[ 9 ], me[ 10 ] ).length();

            te[ 0 ] = me[ 0 ] * scaleX;
            te[ 1 ] = me[ 1 ] * scaleX;
            te[ 2 ] = me[ 2 ] * scaleX;

            te[ 4 ] = me[ 4 ] * scaleY;
            te[ 5 ] = me[ 5 ] * scaleY;
            te[ 6 ] = me[ 6 ] * scaleY;

            te[ 8 ] = me[ 8 ] * scaleZ;
            te[ 9 ] = me[ 9 ] * scaleZ;
            te[ 10 ] = me[ 10 ] * scaleZ;

            return this;

        };

    }(),

    makeRotationFromEuler: function ( euler ) {

        if ( euler instanceof THREE.Euler === false ) {

            console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );

        }

        var te = this.elements;

        var x = euler.x, y = euler.y, z = euler.z;
        var a = Math.cos( x ), b = Math.sin( x );
        var c = Math.cos( y ), d = Math.sin( y );
        var e = Math.cos( z ), f = Math.sin( z );

        if ( euler.order === 'XYZ' ) {

            var ae = a * e, af = a * f, be = b * e, bf = b * f;

            te[ 0 ] = c * e;
            te[ 4 ] = - c * f;
            te[ 8 ] = d;

            te[ 1 ] = af + be * d;
            te[ 5 ] = ae - bf * d;
            te[ 9 ] = - b * c;

            te[ 2 ] = bf - ae * d;
            te[ 6 ] = be + af * d;
            te[ 10 ] = a * c;

        } else if ( euler.order === 'YXZ' ) {

            var ce = c * e, cf = c * f, de = d * e, df = d * f;

            te[ 0 ] = ce + df * b;
            te[ 4 ] = de * b - cf;
            te[ 8 ] = a * d;

            te[ 1 ] = a * f;
            te[ 5 ] = a * e;
            te[ 9 ] = - b;

            te[ 2 ] = cf * b - de;
            te[ 6 ] = df + ce * b;
            te[ 10 ] = a * c;

        } else if ( euler.order === 'ZXY' ) {

            var ce = c * e, cf = c * f, de = d * e, df = d * f;

            te[ 0 ] = ce - df * b;
            te[ 4 ] = - a * f;
            te[ 8 ] = de + cf * b;

            te[ 1 ] = cf + de * b;
            te[ 5 ] = a * e;
            te[ 9 ] = df - ce * b;

            te[ 2 ] = - a * d;
            te[ 6 ] = b;
            te[ 10 ] = a * c;

        } else if ( euler.order === 'ZYX' ) {

            var ae = a * e, af = a * f, be = b * e, bf = b * f;

            te[ 0 ] = c * e;
            te[ 4 ] = be * d - af;
            te[ 8 ] = ae * d + bf;

            te[ 1 ] = c * f;
            te[ 5 ] = bf * d + ae;
            te[ 9 ] = af * d - be;

            te[ 2 ] = - d;
            te[ 6 ] = b * c;
            te[ 10 ] = a * c;

        } else if ( euler.order === 'YZX' ) {

            var ac = a * c, ad = a * d, bc = b * c, bd = b * d;

            te[ 0 ] = c * e;
            te[ 4 ] = bd - ac * f;
            te[ 8 ] = bc * f + ad;

            te[ 1 ] = f;
            te[ 5 ] = a * e;
            te[ 9 ] = - b * e;

            te[ 2 ] = - d * e;
            te[ 6 ] = ad * f + bc;
            te[ 10 ] = ac - bd * f;

        } else if ( euler.order === 'XZY' ) {

            var ac = a * c, ad = a * d, bc = b * c, bd = b * d;

            te[ 0 ] = c * e;
            te[ 4 ] = - f;
            te[ 8 ] = d * e;

            te[ 1 ] = ac * f + bd;
            te[ 5 ] = a * e;
            te[ 9 ] = ad * f - bc;

            te[ 2 ] = bc * f - ad;
            te[ 6 ] = b * e;
            te[ 10 ] = bd * f + ac;

        }

        // last column
        te[ 3 ] = 0;
        te[ 7 ] = 0;
        te[ 11 ] = 0;

        // bottom row
        te[ 12 ] = 0;
        te[ 13 ] = 0;
        te[ 14 ] = 0;
        te[ 15 ] = 1;

        return this;

    },

    setRotationFromQuaternion: function ( q ) {

        console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' );

        return this.makeRotationFromQuaternion( q );

    },

    makeRotationFromQuaternion: function ( q ) {

        var te = this.elements;

        var x = q.x, y = q.y, z = q.z, w = q.w;
        var x2 = x + x, y2 = y + y, z2 = z + z;
        var xx = x * x2, xy = x * y2, xz = x * z2;
        var yy = y * y2, yz = y * z2, zz = z * z2;
        var wx = w * x2, wy = w * y2, wz = w * z2;

        te[ 0 ] = 1 - ( yy + zz );
        te[ 4 ] = xy - wz;
        te[ 8 ] = xz + wy;

        te[ 1 ] = xy + wz;
        te[ 5 ] = 1 - ( xx + zz );
        te[ 9 ] = yz - wx;

        te[ 2 ] = xz - wy;
        te[ 6 ] = yz + wx;
        te[ 10 ] = 1 - ( xx + yy );

        // last column
        te[ 3 ] = 0;
        te[ 7 ] = 0;
        te[ 11 ] = 0;

        // bottom row
        te[ 12 ] = 0;
        te[ 13 ] = 0;
        te[ 14 ] = 0;
        te[ 15 ] = 1;

        return this;

    },

    lookAt: function () {

        var x, y, z;

        return function ( eye, target, up ) {

            if ( x === undefined ) x = new THREE.Vector3();
            if ( y === undefined ) y = new THREE.Vector3();
            if ( z === undefined ) z = new THREE.Vector3();

            var te = this.elements;

            z.subVectors( eye, target ).normalize();

            if ( z.lengthSq() === 0 ) {

                z.z = 1;

            }

            x.crossVectors( up, z ).normalize();

            if ( x.lengthSq() === 0 ) {

                z.x += 0.0001;
                x.crossVectors( up, z ).normalize();

            }

            y.crossVectors( z, x );


            te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
            te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
            te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;

            return this;

        };

    }(),

    multiply: function ( m, n ) {

        if ( n !== undefined ) {

            console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
            return this.multiplyMatrices( m, n );

        }

        return this.multiplyMatrices( this, m );

    },

    multiplyMatrices: function ( a, b ) {

        var ae = a.elements;
        var be = b.elements;
        var te = this.elements;

        var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
        var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
        var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
        var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];

        var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
        var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
        var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
        var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];

        te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
        te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
        te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
        te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;

        te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
        te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
        te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
        te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;

        te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
        te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
        te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
        te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;

        te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
        te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
        te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
        te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;

        return this;

    },

    multiplyToArray: function ( a, b, r ) {

        var te = this.elements;

        this.multiplyMatrices( a, b );

        r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ];
        r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ];
        r[ 8 ]  = te[ 8 ]; r[ 9 ]  = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ];
        r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ];

        return this;

    },

    multiplyScalar: function ( s ) {

        var te = this.elements;

        te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
        te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
        te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
        te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;

        return this;

    },

    multiplyVector3: function ( vector ) {

        console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' );
        return vector.applyProjection( this );

    },

    multiplyVector4: function ( vector ) {

        console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
        return vector.applyMatrix4( this );

    },

    multiplyVector3Array: function ( a ) {

        console.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' );
        return this.applyToVector3Array( a );

    },

    applyToVector3Array: function () {

        var v1;

        return function ( array, offset, length ) {

            if ( v1 === undefined ) v1 = new THREE.Vector3();
            if ( offset === undefined ) offset = 0;
            if ( length === undefined ) length = array.length;

            for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) {

                v1.fromArray( array, j );
                v1.applyMatrix4( this );
                v1.toArray( array, j );

            }

            return array;

        };

    }(),

    applyToBuffer: function () {

        var v1;

        return function applyToBuffer( buffer, offset, length ) {

            if ( v1 === undefined ) v1 = new THREE.Vector3();
            if ( offset === undefined ) offset = 0;
            if ( length === undefined ) length = buffer.length / buffer.itemSize;

            for ( var i = 0, j = offset; i < length; i ++, j ++ ) {

                v1.x = buffer.getX( j );
                v1.y = buffer.getY( j );
                v1.z = buffer.getZ( j );

                v1.applyMatrix4( this );

                buffer.setXYZ( v1.x, v1.y, v1.z );

            }

            return buffer;

        };

    }(),

    rotateAxis: function ( v ) {

        console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' );

        v.transformDirection( this );

    },

    crossVector: function ( vector ) {

        console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
        return vector.applyMatrix4( this );

    },

    determinant: function () {

        var te = this.elements;

        var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
        var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
        var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
        var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];

        //TODO: make this more efficient
        //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )

        return (
            n41 * (
                + n14 * n23 * n32
                - n13 * n24 * n32
                - n14 * n22 * n33
                + n12 * n24 * n33
                + n13 * n22 * n34
                - n12 * n23 * n34
            ) +
            n42 * (
                + n11 * n23 * n34
                - n11 * n24 * n33
                + n14 * n21 * n33
                - n13 * n21 * n34
                + n13 * n24 * n31
                - n14 * n23 * n31
            ) +
            n43 * (
                + n11 * n24 * n32
                - n11 * n22 * n34
                - n14 * n21 * n32
                + n12 * n21 * n34
                + n14 * n22 * n31
                - n12 * n24 * n31
            ) +
            n44 * (
                - n13 * n22 * n31
                - n11 * n23 * n32
                + n11 * n22 * n33
                + n13 * n21 * n32
                - n12 * n21 * n33
                + n12 * n23 * n31
            )

        );

    },

    transpose: function () {

        var te = this.elements;
        var tmp;

        tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
        tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
        tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;

        tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
        tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
        tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;

        return this;

    },

    flattenToArrayOffset: function ( array, offset ) {

        var te = this.elements;

        array[ offset ] = te[ 0 ];
        array[ offset + 1 ] = te[ 1 ];
        array[ offset + 2 ] = te[ 2 ];
        array[ offset + 3 ] = te[ 3 ];

        array[ offset + 4 ] = te[ 4 ];
        array[ offset + 5 ] = te[ 5 ];
        array[ offset + 6 ] = te[ 6 ];
        array[ offset + 7 ] = te[ 7 ];

        array[ offset + 8 ]  = te[ 8 ];
        array[ offset + 9 ]  = te[ 9 ];
        array[ offset + 10 ] = te[ 10 ];
        array[ offset + 11 ] = te[ 11 ];

        array[ offset + 12 ] = te[ 12 ];
        array[ offset + 13 ] = te[ 13 ];
        array[ offset + 14 ] = te[ 14 ];
        array[ offset + 15 ] = te[ 15 ];

        return array;

    },

    getPosition: function () {

        var v1;

        return function () {

            if ( v1 === undefined ) v1 = new THREE.Vector3();
            console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' );

            var te = this.elements;
            return v1.set( te[ 12 ], te[ 13 ], te[ 14 ] );

        };

    }(),

    setPosition: function ( v ) {

        var te = this.elements;

        te[ 12 ] = v.x;
        te[ 13 ] = v.y;
        te[ 14 ] = v.z;

        return this;

    },

    getInverse: function ( m, throwOnInvertible ) {

        // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
        var te = this.elements;
        var me = m.elements;

        var n11 = me[ 0 ], n12 = me[ 4 ], n13 = me[ 8 ], n14 = me[ 12 ];
        var n21 = me[ 1 ], n22 = me[ 5 ], n23 = me[ 9 ], n24 = me[ 13 ];
        var n31 = me[ 2 ], n32 = me[ 6 ], n33 = me[ 10 ], n34 = me[ 14 ];
        var n41 = me[ 3 ], n42 = me[ 7 ], n43 = me[ 11 ], n44 = me[ 15 ];

        te[ 0 ] = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44;
        te[ 4 ] = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44;
        te[ 8 ] = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44;
        te[ 12 ] = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
        te[ 1 ] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44;
        te[ 5 ] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44;
        te[ 9 ] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44;
        te[ 13 ] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34;
        te[ 2 ] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44;
        te[ 6 ] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44;
        te[ 10 ] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44;
        te[ 14 ] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34;
        te[ 3 ] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43;
        te[ 7 ] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43;
        te[ 11 ] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43;
        te[ 15 ] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33;

        var det = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * te[ 12 ];

        if ( det === 0 ) {

            var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0";

            if ( throwOnInvertible || false ) {

                throw new Error( msg );

            } else {

                console.warn( msg );

            }

            this.identity();

            return this;

        }

        this.multiplyScalar( 1 / det );

        return this;

    },

    translate: function ( v ) {

        console.error( 'THREE.Matrix4: .translate() has been removed.' );

    },

    rotateX: function ( angle ) {

        console.error( 'THREE.Matrix4: .rotateX() has been removed.' );

    },

    rotateY: function ( angle ) {

        console.error( 'THREE.Matrix4: .rotateY() has been removed.' );

    },

    rotateZ: function ( angle ) {

        console.error( 'THREE.Matrix4: .rotateZ() has been removed.' );

    },

    rotateByAxis: function ( axis, angle ) {

        console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' );

    },

    scale: function ( v ) {

        var te = this.elements;
        var x = v.x, y = v.y, z = v.z;

        te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
        te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
        te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
        te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;

        return this;

    },

    getMaxScaleOnAxis: function () {

        var te = this.elements;

        var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
        var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
        var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];

        return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );

    },

    makeTranslation: function ( x, y, z ) {

        this.set(

            1, 0, 0, x,
            0, 1, 0, y,
            0, 0, 1, z,
            0, 0, 0, 1

        );

        return this;

    },

    makeRotationX: function ( theta ) {

        var c = Math.cos( theta ), s = Math.sin( theta );

        this.set(

            1, 0,  0, 0,
            0, c, - s, 0,
            0, s,  c, 0,
            0, 0,  0, 1

        );

        return this;

    },

    makeRotationY: function ( theta ) {

        var c = Math.cos( theta ), s = Math.sin( theta );

        this.set(

            c, 0, s, 0,
            0, 1, 0, 0,
            - s, 0, c, 0,
            0, 0, 0, 1

        );

        return this;

    },

    makeRotationZ: function ( theta ) {

        var c = Math.cos( theta ), s = Math.sin( theta );

        this.set(

            c, - s, 0, 0,
            s,  c, 0, 0,
            0,  0, 1, 0,
            0,  0, 0, 1

        );

        return this;

    },

    makeRotationAxis: function ( axis, angle ) {

        // Based on http://www.gamedev.net/reference/articles/article1199.asp

        var c = Math.cos( angle );
        var s = Math.sin( angle );
        var t = 1 - c;
        var x = axis.x, y = axis.y, z = axis.z;
        var tx = t * x, ty = t * y;

        this.set(

            tx * x + c, tx * y - s * z, tx * z + s * y, 0,
            tx * y + s * z, ty * y + c, ty * z - s * x, 0,
            tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
            0, 0, 0, 1

        );

        return this;

    },

    makeScale: function ( x, y, z ) {

        this.set(

            x, 0, 0, 0,
            0, y, 0, 0,
            0, 0, z, 0,
            0, 0, 0, 1

        );

        return this;

    },

    compose: function ( position, quaternion, scale ) {

        this.makeRotationFromQuaternion( quaternion );
        this.scale( scale );
        this.setPosition( position );

        return this;

    },

    decompose: function () {

        var vector, matrix;

        return function ( position, quaternion, scale ) {

            if ( vector === undefined ) vector = new THREE.Vector3();
            if ( matrix === undefined ) matrix = new THREE.Matrix4();

            var te = this.elements;

            var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
            var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
            var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();

            // if determine is negative, we need to invert one scale
            var det = this.determinant();
            if ( det < 0 ) {

                sx = - sx;

            }

            position.x = te[ 12 ];
            position.y = te[ 13 ];
            position.z = te[ 14 ];

            // scale the rotation part

            matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy()

            var invSX = 1 / sx;
            var invSY = 1 / sy;
            var invSZ = 1 / sz;

            matrix.elements[ 0 ] *= invSX;
            matrix.elements[ 1 ] *= invSX;
            matrix.elements[ 2 ] *= invSX;

            matrix.elements[ 4 ] *= invSY;
            matrix.elements[ 5 ] *= invSY;
            matrix.elements[ 6 ] *= invSY;

            matrix.elements[ 8 ] *= invSZ;
            matrix.elements[ 9 ] *= invSZ;
            matrix.elements[ 10 ] *= invSZ;

            quaternion.setFromRotationMatrix( matrix );

            scale.x = sx;
            scale.y = sy;
            scale.z = sz;

            return this;

        };

    }(),

    makeFrustum: function ( left, right, bottom, top, near, far ) {

        var te = this.elements;
        var x = 2 * near / ( right - left );
        var y = 2 * near / ( top - bottom );

        var a = ( right + left ) / ( right - left );
        var b = ( top + bottom ) / ( top - bottom );
        var c = - ( far + near ) / ( far - near );
        var d = - 2 * far * near / ( far - near );

        te[ 0 ] = x;	te[ 4 ] = 0;	te[ 8 ] = a;	te[ 12 ] = 0;
        te[ 1 ] = 0;	te[ 5 ] = y;	te[ 9 ] = b;	te[ 13 ] = 0;
        te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = c;	te[ 14 ] = d;
        te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = - 1;	te[ 15 ] = 0;

        return this;

    },

    makePerspective: function ( fov, aspect, near, far ) {

        var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) );
        var ymin = - ymax;
        var xmin = ymin * aspect;
        var xmax = ymax * aspect;

        return this.makeFrustum( xmin, xmax, ymin, ymax, near, far );

    },

    makeOrthographic: function ( left, right, top, bottom, near, far ) {

        var te = this.elements;
        var w = right - left;
        var h = top - bottom;
        var p = far - near;

        var x = ( right + left ) / w;
        var y = ( top + bottom ) / h;
        var z = ( far + near ) / p;

        te[ 0 ] = 2 / w;	te[ 4 ] = 0;	te[ 8 ] = 0;	te[ 12 ] = - x;
        te[ 1 ] = 0;	te[ 5 ] = 2 / h;	te[ 9 ] = 0;	te[ 13 ] = - y;
        te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = - 2 / p;	te[ 14 ] = - z;
        te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = 0;	te[ 15 ] = 1;

        return this;

    },

    equals: function ( matrix ) {

        var te = this.elements;
        var me = matrix.elements;

        for ( var i = 0; i < 16; i ++ ) {

            if ( te[ i ] !== me[ i ] ) return false;

        }

        return true;

    },

    fromArray: function ( array ) {

        this.elements.set( array );

        return this;

    },

    toArray: function () {

        var te = this.elements;

        return [
            te[ 0 ], te[ 1 ], te[ 2 ], te[ 3 ],
            te[ 4 ], te[ 5 ], te[ 6 ], te[ 7 ],
            te[ 8 ], te[ 9 ], te[ 10 ], te[ 11 ],
            te[ 12 ], te[ 13 ], te[ 14 ], te[ 15 ]
        ];

    }

};

// File:src/math/Ray.js

/**
 * @author bhouston / http://clara.io
 */

THREE.Ray = function ( origin, direction ) {

    this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3();
    this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3();

};

THREE.Ray.prototype = {

    constructor: THREE.Ray,

    set: function ( origin, direction ) {

        this.origin.copy( origin );
        this.direction.copy( direction );

        return this;

    },

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( ray ) {

        this.origin.copy( ray.origin );
        this.direction.copy( ray.direction );

        return this;

    },

    at: function ( t, optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();

        return result.copy( this.direction ).multiplyScalar( t ).add( this.origin );

    },

    recast: function () {

        var v1 = new THREE.Vector3();

        return function ( t ) {

            this.origin.copy( this.at( t, v1 ) );

            return this;

        };

    }(),

    closestPointToPoint: function ( point, optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();
        result.subVectors( point, this.origin );
        var directionDistance = result.dot( this.direction );

        if ( directionDistance < 0 ) {

            return result.copy( this.origin );

        }

        return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );

    },

    distanceToPoint: function ( point ) {

        return Math.sqrt( this.distanceSqToPoint( point ) );

    },

    distanceSqToPoint: function () {

        var v1 = new THREE.Vector3();

        return function ( point ) {

            var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );

            // point behind the ray

            if ( directionDistance < 0 ) {

                return this.origin.distanceToSquared( point );

            }

            v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );

            return v1.distanceToSquared( point );

        };

    }(),

    distanceSqToSegment: function () {

        var segCenter = new THREE.Vector3();
        var segDir = new THREE.Vector3();
        var diff = new THREE.Vector3();

        return function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {

            // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp
            // It returns the min distance between the ray and the segment
            // defined by v0 and v1
            // It can also set two optional targets :
            // - The closest point on the ray
            // - The closest point on the segment

            segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );
            segDir.copy( v1 ).sub( v0 ).normalize();
            diff.copy( this.origin ).sub( segCenter );

            var segExtent = v0.distanceTo( v1 ) * 0.5;
            var a01 = - this.direction.dot( segDir );
            var b0 = diff.dot( this.direction );
            var b1 = - diff.dot( segDir );
            var c = diff.lengthSq();
            var det = Math.abs( 1 - a01 * a01 );
            var s0, s1, sqrDist, extDet;

            if ( det > 0 ) {

                // The ray and segment are not parallel.

                s0 = a01 * b1 - b0;
                s1 = a01 * b0 - b1;
                extDet = segExtent * det;

                if ( s0 >= 0 ) {

                    if ( s1 >= - extDet ) {

                        if ( s1 <= extDet ) {

                            // region 0
                            // Minimum at interior points of ray and segment.

                            var invDet = 1 / det;
                            s0 *= invDet;
                            s1 *= invDet;
                            sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;

                        } else {

                            // region 1

                            s1 = segExtent;
                            s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
                            sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;

                        }

                    } else {

                        // region 5

                        s1 = - segExtent;
                        s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
                        sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;

                    }

                } else {

                    if ( s1 <= - extDet ) {

                        // region 4

                        s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
                        s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
                        sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;

                    } else if ( s1 <= extDet ) {

                        // region 3

                        s0 = 0;
                        s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
                        sqrDist = s1 * ( s1 + 2 * b1 ) + c;

                    } else {

                        // region 2

                        s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
                        s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
                        sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;

                    }

                }

            } else {

                // Ray and segment are parallel.

                s1 = ( a01 > 0 ) ? - segExtent : segExtent;
                s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
                sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;

            }

            if ( optionalPointOnRay ) {

                optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );

            }

            if ( optionalPointOnSegment ) {

                optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter );

            }

            return sqrDist;

        };

    }(),


    isIntersectionSphere: function ( sphere ) {

        return this.distanceToPoint( sphere.center ) <= sphere.radius;

    },

    intersectSphere: function () {

        // from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/

        var v1 = new THREE.Vector3();

        return function ( sphere, optionalTarget ) {

            v1.subVectors( sphere.center, this.origin );

            var tca = v1.dot( this.direction );

            var d2 = v1.dot( v1 ) - tca * tca;

            var radius2 = sphere.radius * sphere.radius;

            if ( d2 > radius2 ) return null;

            var thc = Math.sqrt( radius2 - d2 );

            // t0 = first intersect point - entrance on front of sphere
            var t0 = tca - thc;

            // t1 = second intersect point - exit point on back of sphere
            var t1 = tca + thc;

            // test to see if both t0 and t1 are behind the ray - if so, return null
            if ( t0 < 0 && t1 < 0 ) return null;

            // test to see if t0 is behind the ray:
            // if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
            // in order to always return an intersect point that is in front of the ray.
            if ( t0 < 0 ) return this.at( t1, optionalTarget );

            // else t0 is in front of the ray, so return the first collision point scaled by t0
            return this.at( t0, optionalTarget );

        }

    }(),

    isIntersectionPlane: function ( plane ) {

        // check if the ray lies on the plane first

        var distToPoint = plane.distanceToPoint( this.origin );

        if ( distToPoint === 0 ) {

            return true;

        }

        var denominator = plane.normal.dot( this.direction );

        if ( denominator * distToPoint < 0 ) {

            return true;

        }

        // ray origin is behind the plane (and is pointing behind it)

        return false;

    },

    distanceToPlane: function ( plane ) {

        var denominator = plane.normal.dot( this.direction );
        if ( denominator === 0 ) {

            // line is coplanar, return origin
            if ( plane.distanceToPoint( this.origin ) === 0 ) {

                return 0;

            }

            // Null is preferable to undefined since undefined means.... it is undefined

            return null;

        }

        var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;

        // Return if the ray never intersects the plane

        return t >= 0 ? t :  null;

    },

    intersectPlane: function ( plane, optionalTarget ) {

        var t = this.distanceToPlane( plane );

        if ( t === null ) {

            return null;

        }

        return this.at( t, optionalTarget );

    },

    isIntersectionBox: function () {

        var v = new THREE.Vector3();

        return function ( box ) {

            return this.intersectBox( box, v ) !== null;

        };

    }(),

    intersectBox: function ( box, optionalTarget ) {

        // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/

        var tmin, tmax, tymin, tymax, tzmin, tzmax;

        var invdirx = 1 / this.direction.x,
            invdiry = 1 / this.direction.y,
            invdirz = 1 / this.direction.z;

        var origin = this.origin;

        if ( invdirx >= 0 ) {

            tmin = ( box.min.x - origin.x ) * invdirx;
            tmax = ( box.max.x - origin.x ) * invdirx;

        } else {

            tmin = ( box.max.x - origin.x ) * invdirx;
            tmax = ( box.min.x - origin.x ) * invdirx;

        }

        if ( invdiry >= 0 ) {

            tymin = ( box.min.y - origin.y ) * invdiry;
            tymax = ( box.max.y - origin.y ) * invdiry;

        } else {

            tymin = ( box.max.y - origin.y ) * invdiry;
            tymax = ( box.min.y - origin.y ) * invdiry;

        }

        if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;

        // These lines also handle the case where tmin or tmax is NaN
        // (result of 0 * Infinity). x !== x returns true if x is NaN

        if ( tymin > tmin || tmin !== tmin ) tmin = tymin;

        if ( tymax < tmax || tmax !== tmax ) tmax = tymax;

        if ( invdirz >= 0 ) {

            tzmin = ( box.min.z - origin.z ) * invdirz;
            tzmax = ( box.max.z - origin.z ) * invdirz;

        } else {

            tzmin = ( box.max.z - origin.z ) * invdirz;
            tzmax = ( box.min.z - origin.z ) * invdirz;

        }

        if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;

        if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;

        if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;

        //return point closest to the ray (positive side)

        if ( tmax < 0 ) return null;

        return this.at( tmin >= 0 ? tmin : tmax, optionalTarget );

    },

    intersectTriangle: function () {

        // Compute the offset origin, edges, and normal.
        var diff = new THREE.Vector3();
        var edge1 = new THREE.Vector3();
        var edge2 = new THREE.Vector3();
        var normal = new THREE.Vector3();

        return function ( a, b, c, backfaceCulling, optionalTarget ) {

            // from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp

            edge1.subVectors( b, a );
            edge2.subVectors( c, a );
            normal.crossVectors( edge1, edge2 );

            // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
            // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
            //   |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
            //   |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
            //   |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
            var DdN = this.direction.dot( normal );
            var sign;

            if ( DdN > 0 ) {

                if ( backfaceCulling ) return null;
                sign = 1;

            } else if ( DdN < 0 ) {

                sign = - 1;
                DdN = - DdN;

            } else {

                return null;

            }

            diff.subVectors( this.origin, a );
            var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) );

            // b1 < 0, no intersection
            if ( DdQxE2 < 0 ) {

                return null;

            }

            var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) );

            // b2 < 0, no intersection
            if ( DdE1xQ < 0 ) {

                return null;

            }

            // b1+b2 > 1, no intersection
            if ( DdQxE2 + DdE1xQ > DdN ) {

                return null;

            }

            // Line intersects triangle, check if ray does.
            var QdN = - sign * diff.dot( normal );

            // t < 0, no intersection
            if ( QdN < 0 ) {

                return null;

            }

            // Ray intersects triangle.
            return this.at( QdN / DdN, optionalTarget );

        };

    }(),

    applyMatrix4: function ( matrix4 ) {

        this.direction.add( this.origin ).applyMatrix4( matrix4 );
        this.origin.applyMatrix4( matrix4 );
        this.direction.sub( this.origin );
        this.direction.normalize();

        return this;

    },

    equals: function ( ray ) {

        return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );

    }

};

// File:src/math/Sphere.js

/**
 * @author bhouston / http://clara.io
 * @author mrdoob / http://mrdoob.com/
 */

THREE.Sphere = function ( center, radius ) {

    this.center = ( center !== undefined ) ? center : new THREE.Vector3();
    this.radius = ( radius !== undefined ) ? radius : 0;

};

THREE.Sphere.prototype = {

    constructor: THREE.Sphere,

    set: function ( center, radius ) {

        this.center.copy( center );
        this.radius = radius;

        return this;

    },

    setFromPoints: function () {

        var box = new THREE.Box3();

        return function ( points, optionalCenter ) {

            var center = this.center;

            if ( optionalCenter !== undefined ) {

                center.copy( optionalCenter );

            } else {

                box.setFromPoints( points ).center( center );

            }

            var maxRadiusSq = 0;

            for ( var i = 0, il = points.length; i < il; i ++ ) {

                maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );

            }

            this.radius = Math.sqrt( maxRadiusSq );

            return this;

        };

    }(),

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( sphere ) {

        this.center.copy( sphere.center );
        this.radius = sphere.radius;

        return this;

    },

    empty: function () {

        return ( this.radius <= 0 );

    },

    containsPoint: function ( point ) {

        return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );

    },

    distanceToPoint: function ( point ) {

        return ( point.distanceTo( this.center ) - this.radius );

    },

    intersectsSphere: function ( sphere ) {

        var radiusSum = this.radius + sphere.radius;

        return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );

    },

    clampPoint: function ( point, optionalTarget ) {

        var deltaLengthSq = this.center.distanceToSquared( point );

        var result = optionalTarget || new THREE.Vector3();
        result.copy( point );

        if ( deltaLengthSq > ( this.radius * this.radius ) ) {

            result.sub( this.center ).normalize();
            result.multiplyScalar( this.radius ).add( this.center );

        }

        return result;

    },

    getBoundingBox: function ( optionalTarget ) {

        var box = optionalTarget || new THREE.Box3();

        box.set( this.center, this.center );
        box.expandByScalar( this.radius );

        return box;

    },

    applyMatrix4: function ( matrix ) {

        this.center.applyMatrix4( matrix );
        this.radius = this.radius * matrix.getMaxScaleOnAxis();

        return this;

    },

    translate: function ( offset ) {

        this.center.add( offset );

        return this;

    },

    equals: function ( sphere ) {

        return sphere.center.equals( this.center ) && ( sphere.radius === this.radius );

    }

};

// File:src/math/Frustum.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 * @author bhouston / http://clara.io
 */

THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) {

    this.planes = [

        ( p0 !== undefined ) ? p0 : new THREE.Plane(),
        ( p1 !== undefined ) ? p1 : new THREE.Plane(),
        ( p2 !== undefined ) ? p2 : new THREE.Plane(),
        ( p3 !== undefined ) ? p3 : new THREE.Plane(),
        ( p4 !== undefined ) ? p4 : new THREE.Plane(),
        ( p5 !== undefined ) ? p5 : new THREE.Plane()

    ];

};

THREE.Frustum.prototype = {

    constructor: THREE.Frustum,

    set: function ( p0, p1, p2, p3, p4, p5 ) {

        var planes = this.planes;

        planes[ 0 ].copy( p0 );
        planes[ 1 ].copy( p1 );
        planes[ 2 ].copy( p2 );
        planes[ 3 ].copy( p3 );
        planes[ 4 ].copy( p4 );
        planes[ 5 ].copy( p5 );

        return this;

    },

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( frustum ) {

        var planes = this.planes;

        for ( var i = 0; i < 6; i ++ ) {

            planes[ i ].copy( frustum.planes[ i ] );

        }

        return this;

    },

    setFromMatrix: function ( m ) {

        var planes = this.planes;
        var me = m.elements;
        var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];
        var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];
        var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];
        var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];

        planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
        planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
        planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
        planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
        planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
        planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();

        return this;

    },

    intersectsObject: function () {

        var sphere = new THREE.Sphere();

        return function ( object ) {

            var geometry = object.geometry;

            if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();

            sphere.copy( geometry.boundingSphere );
            sphere.applyMatrix4( object.matrixWorld );

            return this.intersectsSphere( sphere );

        };

    }(),

    intersectsSphere: function ( sphere ) {

        var planes = this.planes;
        var center = sphere.center;
        var negRadius = - sphere.radius;

        for ( var i = 0; i < 6; i ++ ) {

            var distance = planes[ i ].distanceToPoint( center );

            if ( distance < negRadius ) {

                return false;

            }

        }

        return true;

    },

    intersectsBox: function () {

        var p1 = new THREE.Vector3(),
            p2 = new THREE.Vector3();

        return function ( box ) {

            var planes = this.planes;

            for ( var i = 0; i < 6 ; i ++ ) {

                var plane = planes[ i ];

                p1.x = plane.normal.x > 0 ? box.min.x : box.max.x;
                p2.x = plane.normal.x > 0 ? box.max.x : box.min.x;
                p1.y = plane.normal.y > 0 ? box.min.y : box.max.y;
                p2.y = plane.normal.y > 0 ? box.max.y : box.min.y;
                p1.z = plane.normal.z > 0 ? box.min.z : box.max.z;
                p2.z = plane.normal.z > 0 ? box.max.z : box.min.z;

                var d1 = plane.distanceToPoint( p1 );
                var d2 = plane.distanceToPoint( p2 );

                // if both outside plane, no intersection

                if ( d1 < 0 && d2 < 0 ) {

                    return false;

                }

            }

            return true;

        };

    }(),


    containsPoint: function ( point ) {

        var planes = this.planes;

        for ( var i = 0; i < 6; i ++ ) {

            if ( planes[ i ].distanceToPoint( point ) < 0 ) {

                return false;

            }

        }

        return true;

    }

};

// File:src/math/Plane.js

/**
 * @author bhouston / http://clara.io
 */

THREE.Plane = function ( normal, constant ) {

    this.normal = ( normal !== undefined ) ? normal : new THREE.Vector3( 1, 0, 0 );
    this.constant = ( constant !== undefined ) ? constant : 0;

};

THREE.Plane.prototype = {

    constructor: THREE.Plane,

    set: function ( normal, constant ) {

        this.normal.copy( normal );
        this.constant = constant;

        return this;

    },

    setComponents: function ( x, y, z, w ) {

        this.normal.set( x, y, z );
        this.constant = w;

        return this;

    },

    setFromNormalAndCoplanarPoint: function ( normal, point ) {

        this.normal.copy( normal );
        this.constant = - point.dot( this.normal );	// must be this.normal, not normal, as this.normal is normalized

        return this;

    },

    setFromCoplanarPoints: function () {

        var v1 = new THREE.Vector3();
        var v2 = new THREE.Vector3();

        return function ( a, b, c ) {

            var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize();

            // Q: should an error be thrown if normal is zero (e.g. degenerate plane)?

            this.setFromNormalAndCoplanarPoint( normal, a );

            return this;

        };

    }(),

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( plane ) {

        this.normal.copy( plane.normal );
        this.constant = plane.constant;

        return this;

    },

    normalize: function () {

        // Note: will lead to a divide by zero if the plane is invalid.

        var inverseNormalLength = 1.0 / this.normal.length();
        this.normal.multiplyScalar( inverseNormalLength );
        this.constant *= inverseNormalLength;

        return this;

    },

    negate: function () {

        this.constant *= - 1;
        this.normal.negate();

        return this;

    },

    distanceToPoint: function ( point ) {

        return this.normal.dot( point ) + this.constant;

    },

    distanceToSphere: function ( sphere ) {

        return this.distanceToPoint( sphere.center ) - sphere.radius;

    },

    projectPoint: function ( point, optionalTarget ) {

        return this.orthoPoint( point, optionalTarget ).sub( point ).negate();

    },

    orthoPoint: function ( point, optionalTarget ) {

        var perpendicularMagnitude = this.distanceToPoint( point );

        var result = optionalTarget || new THREE.Vector3();
        return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude );

    },

    isIntersectionLine: function ( line ) {

        // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.

        var startSign = this.distanceToPoint( line.start );
        var endSign = this.distanceToPoint( line.end );

        return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );

    },

    intersectLine: function () {

        var v1 = new THREE.Vector3();

        return function ( line, optionalTarget ) {

            var result = optionalTarget || new THREE.Vector3();

            var direction = line.delta( v1 );

            var denominator = this.normal.dot( direction );

            if ( denominator === 0 ) {

                // line is coplanar, return origin
                if ( this.distanceToPoint( line.start ) === 0 ) {

                    return result.copy( line.start );

                }

                // Unsure if this is the correct method to handle this case.
                return undefined;

            }

            var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;

            if ( t < 0 || t > 1 ) {

                return undefined;

            }

            return result.copy( direction ).multiplyScalar( t ).add( line.start );

        };

    }(),


    coplanarPoint: function ( optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();
        return result.copy( this.normal ).multiplyScalar( - this.constant );

    },

    applyMatrix4: function () {

        var v1 = new THREE.Vector3();
        var v2 = new THREE.Vector3();
        var m1 = new THREE.Matrix3();

        return function ( matrix, optionalNormalMatrix ) {

            // compute new normal based on theory here:
            // http://www.songho.ca/opengl/gl_normaltransform.html
            var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix );
            var newNormal = v1.copy( this.normal ).applyMatrix3( normalMatrix );

            var newCoplanarPoint = this.coplanarPoint( v2 );
            newCoplanarPoint.applyMatrix4( matrix );

            this.setFromNormalAndCoplanarPoint( newNormal, newCoplanarPoint );

            return this;

        };

    }(),

    translate: function ( offset ) {

        this.constant = this.constant - offset.dot( this.normal );

        return this;

    },

    equals: function ( plane ) {

        return plane.normal.equals( this.normal ) && ( plane.constant === this.constant );

    }

};

// File:src/math/Math.js

/**
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 */

THREE.Math = {

    generateUUID: function () {

        // http://www.broofa.com/Tools/Math.uuid.htm

        var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
        var uuid = new Array( 36 );
        var rnd = 0, r;

        return function () {

            for ( var i = 0; i < 36; i ++ ) {

                if ( i === 8 || i === 13 || i === 18 || i === 23 ) {

                    uuid[ i ] = '-';

                } else if ( i === 14 ) {

                    uuid[ i ] = '4';

                } else {

                    if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
                    r = rnd & 0xf;
                    rnd = rnd >> 4;
                    uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];

                }

            }

            return uuid.join( '' );

        };

    }(),

    clamp: function ( value, min, max ) {

        return Math.max( min, Math.min( max, value ) );

    },

    // compute euclidian modulo of m % n
    // https://en.wikipedia.org/wiki/Modulo_operation

    euclideanModulo: function ( n, m ) {

        return ( ( n % m ) + m ) % m;

    },

    // Linear mapping from range <a1, a2> to range <b1, b2>

    mapLinear: function ( x, a1, a2, b1, b2 ) {

        return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );

    },

    // http://en.wikipedia.org/wiki/Smoothstep

    smoothstep: function ( x, min, max ) {

        if ( x <= min ) return 0;
        if ( x >= max ) return 1;

        x = ( x - min ) / ( max - min );

        return x * x * ( 3 - 2 * x );

    },

    smootherstep: function ( x, min, max ) {

        if ( x <= min ) return 0;
        if ( x >= max ) return 1;

        x = ( x - min ) / ( max - min );

        return x * x * x * ( x * ( x * 6 - 15 ) + 10 );

    },

    // Random float from <0, 1> with 16 bits of randomness
    // (standard Math.random() creates repetitive patterns when applied over larger space)

    random16: function () {

        return ( 65280 * Math.random() + 255 * Math.random() ) / 65535;

    },

    // Random integer from <low, high> interval

    randInt: function ( low, high ) {

        return low + Math.floor( Math.random() * ( high - low + 1 ) );

    },

    // Random float from <low, high> interval

    randFloat: function ( low, high ) {

        return low + Math.random() * ( high - low );

    },

    // Random float from <-range/2, range/2> interval

    randFloatSpread: function ( range ) {

        return range * ( 0.5 - Math.random() );

    },

    degToRad: function () {

        var degreeToRadiansFactor = Math.PI / 180;

        return function ( degrees ) {

            return degrees * degreeToRadiansFactor;

        };

    }(),

    radToDeg: function () {

        var radianToDegreesFactor = 180 / Math.PI;

        return function ( radians ) {

            return radians * radianToDegreesFactor;

        };

    }(),

    isPowerOfTwo: function ( value ) {

        return ( value & ( value - 1 ) ) === 0 && value !== 0;

    },

    nearestPowerOfTwo: function ( value ) {

        return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) );

    },

    nextPowerOfTwo: function ( value ) {

        value --;
        value |= value >> 1;
        value |= value >> 2;
        value |= value >> 4;
        value |= value >> 8;
        value |= value >> 16;
        value ++;

        return value;

    }

};

// File:src/math/Spline.js

/**
 * Spline from Tween.js, slightly optimized (and trashed)
 * http://sole.github.com/tween.js/examples/05_spline.html
 *
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Spline = function ( points ) {

    this.points = points;

    var c = [], v3 = { x: 0, y: 0, z: 0 },
        point, intPoint, weight, w2, w3,
        pa, pb, pc, pd;

    this.initFromArray = function ( a ) {

        this.points = [];

        for ( var i = 0; i < a.length; i ++ ) {

            this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] };

        }

    };

    this.getPoint = function ( k ) {

        point = ( this.points.length - 1 ) * k;
        intPoint = Math.floor( point );
        weight = point - intPoint;

        c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
        c[ 1 ] = intPoint;
        c[ 2 ] = intPoint  > this.points.length - 2 ? this.points.length - 1 : intPoint + 1;
        c[ 3 ] = intPoint  > this.points.length - 3 ? this.points.length - 1 : intPoint + 2;

        pa = this.points[ c[ 0 ] ];
        pb = this.points[ c[ 1 ] ];
        pc = this.points[ c[ 2 ] ];
        pd = this.points[ c[ 3 ] ];

        w2 = weight * weight;
        w3 = weight * w2;

        v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 );
        v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 );
        v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 );

        return v3;

    };

    this.getControlPointsArray = function () {

        var i, p, l = this.points.length,
            coords = [];

        for ( i = 0; i < l; i ++ ) {

            p = this.points[ i ];
            coords[ i ] = [ p.x, p.y, p.z ];

        }

        return coords;

    };

    // approximate length by summing linear segments

    this.getLength = function ( nSubDivisions ) {

        var i, index, nSamples, position,
            point = 0, intPoint = 0, oldIntPoint = 0,
            oldPosition = new THREE.Vector3(),
            tmpVec = new THREE.Vector3(),
            chunkLengths = [],
            totalLength = 0;

        // first point has 0 length

        chunkLengths[ 0 ] = 0;

        if ( ! nSubDivisions ) nSubDivisions = 100;

        nSamples = this.points.length * nSubDivisions;

        oldPosition.copy( this.points[ 0 ] );

        for ( i = 1; i < nSamples; i ++ ) {

            index = i / nSamples;

            position = this.getPoint( index );
            tmpVec.copy( position );

            totalLength += tmpVec.distanceTo( oldPosition );

            oldPosition.copy( position );

            point = ( this.points.length - 1 ) * index;
            intPoint = Math.floor( point );

            if ( intPoint !== oldIntPoint ) {

                chunkLengths[ intPoint ] = totalLength;
                oldIntPoint = intPoint;

            }

        }

        // last point ends with total length

        chunkLengths[ chunkLengths.length ] = totalLength;

        return { chunks: chunkLengths, total: totalLength };

    };

    this.reparametrizeByArcLength = function ( samplingCoef ) {

        var i, j,
            index, indexCurrent, indexNext,
            realDistance,
            sampling, position,
            newpoints = [],
            tmpVec = new THREE.Vector3(),
            sl = this.getLength();

        newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() );

        for ( i = 1; i < this.points.length; i ++ ) {

            //tmpVec.copy( this.points[ i - 1 ] );
            //linearDistance = tmpVec.distanceTo( this.points[ i ] );

            realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ];

            sampling = Math.ceil( samplingCoef * realDistance / sl.total );

            indexCurrent = ( i - 1 ) / ( this.points.length - 1 );
            indexNext = i / ( this.points.length - 1 );

            for ( j = 1; j < sampling - 1; j ++ ) {

                index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent );

                position = this.getPoint( index );
                newpoints.push( tmpVec.copy( position ).clone() );

            }

            newpoints.push( tmpVec.copy( this.points[ i ] ).clone() );

        }

        this.points = newpoints;

    };

    // Catmull-Rom

    function interpolate( p0, p1, p2, p3, t, t2, t3 ) {

        var v0 = ( p2 - p0 ) * 0.5,
            v1 = ( p3 - p1 ) * 0.5;

        return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;

    }

};

// File:src/math/Triangle.js

/**
 * @author bhouston / http://clara.io
 * @author mrdoob / http://mrdoob.com/
 */

THREE.Triangle = function ( a, b, c ) {

    this.a = ( a !== undefined ) ? a : new THREE.Vector3();
    this.b = ( b !== undefined ) ? b : new THREE.Vector3();
    this.c = ( c !== undefined ) ? c : new THREE.Vector3();

};

THREE.Triangle.normal = function () {

    var v0 = new THREE.Vector3();

    return function ( a, b, c, optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();

        result.subVectors( c, b );
        v0.subVectors( a, b );
        result.cross( v0 );

        var resultLengthSq = result.lengthSq();
        if ( resultLengthSq > 0 ) {

            return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) );

        }

        return result.set( 0, 0, 0 );

    };

}();

// static/instance method to calculate barycentric coordinates
// based on: http://www.blackpawn.com/texts/pointinpoly/default.html
THREE.Triangle.barycoordFromPoint = function () {

    var v0 = new THREE.Vector3();
    var v1 = new THREE.Vector3();
    var v2 = new THREE.Vector3();

    return function ( point, a, b, c, optionalTarget ) {

        v0.subVectors( c, a );
        v1.subVectors( b, a );
        v2.subVectors( point, a );

        var dot00 = v0.dot( v0 );
        var dot01 = v0.dot( v1 );
        var dot02 = v0.dot( v2 );
        var dot11 = v1.dot( v1 );
        var dot12 = v1.dot( v2 );

        var denom = ( dot00 * dot11 - dot01 * dot01 );

        var result = optionalTarget || new THREE.Vector3();

        // collinear or singular triangle
        if ( denom === 0 ) {

            // arbitrary location outside of triangle?
            // not sure if this is the best idea, maybe should be returning undefined
            return result.set( - 2, - 1, - 1 );

        }

        var invDenom = 1 / denom;
        var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
        var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;

        // barycentric coordinates must always sum to 1
        return result.set( 1 - u - v, v, u );

    };

}();

THREE.Triangle.containsPoint = function () {

    var v1 = new THREE.Vector3();

    return function ( point, a, b, c ) {

        var result = THREE.Triangle.barycoordFromPoint( point, a, b, c, v1 );

        return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 );

    };

}();

THREE.Triangle.prototype = {

    constructor: THREE.Triangle,

    set: function ( a, b, c ) {

        this.a.copy( a );
        this.b.copy( b );
        this.c.copy( c );

        return this;

    },

    setFromPointsAndIndices: function ( points, i0, i1, i2 ) {

        this.a.copy( points[ i0 ] );
        this.b.copy( points[ i1 ] );
        this.c.copy( points[ i2 ] );

        return this;

    },

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( triangle ) {

        this.a.copy( triangle.a );
        this.b.copy( triangle.b );
        this.c.copy( triangle.c );

        return this;

    },

    area: function () {

        var v0 = new THREE.Vector3();
        var v1 = new THREE.Vector3();

        return function () {

            v0.subVectors( this.c, this.b );
            v1.subVectors( this.a, this.b );

            return v0.cross( v1 ).length() * 0.5;

        };

    }(),

    midpoint: function ( optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();
        return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );

    },

    normal: function ( optionalTarget ) {

        return THREE.Triangle.normal( this.a, this.b, this.c, optionalTarget );

    },

    plane: function ( optionalTarget ) {

        var result = optionalTarget || new THREE.Plane();

        return result.setFromCoplanarPoints( this.a, this.b, this.c );

    },

    barycoordFromPoint: function ( point, optionalTarget ) {

        return THREE.Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget );

    },

    containsPoint: function ( point ) {

        return THREE.Triangle.containsPoint( point, this.a, this.b, this.c );

    },

    equals: function ( triangle ) {

        return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );

    }

};

// File:src/core/Channels.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.Channels = function () {

    this.mask = 1;

};

THREE.Channels.prototype = {

    constructor: THREE.Channels,

    set: function ( channel ) {

        this.mask = 1 << channel;

    },

    enable: function ( channel ) {

        this.mask |= 1 << channel;

    },

    toggle: function ( channel ) {

        this.mask ^= 1 << channel;

    },

    disable: function ( channel ) {

        this.mask &= ~ ( 1 << channel );

    }

};

// File:src/core/Clock.js

/**
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Clock = function ( autoStart ) {

    this.autoStart = ( autoStart !== undefined ) ? autoStart : true;

    this.startTime = 0;
    this.oldTime = 0;
    this.elapsedTime = 0;

    this.running = false;

};

THREE.Clock.prototype = {

    constructor: THREE.Clock,

    start: function () {

        this.startTime = self.performance.now();

        this.oldTime = this.startTime;
        this.running = true;

    },

    stop: function () {

        this.getElapsedTime();
        this.running = false;

    },

    getElapsedTime: function () {

        this.getDelta();
        return this.elapsedTime;

    },

    getDelta: function () {

        var diff = 0;

        if ( this.autoStart && ! this.running ) {

            this.start();

        }

        if ( this.running ) {

            var newTime = self.performance.now();

            diff = 0.001 * ( newTime - this.oldTime );
            this.oldTime = newTime;

            this.elapsedTime += diff;

        }

        return diff;

    }

};

// File:src/core/EventDispatcher.js

/**
 * https://github.com/mrdoob/eventdispatcher.js/
 */

THREE.EventDispatcher = function () {};

THREE.EventDispatcher.prototype = {

    constructor: THREE.EventDispatcher,

    apply: function ( object ) {

        object.addEventListener = THREE.EventDispatcher.prototype.addEventListener;
        object.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener;
        object.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener;
        object.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent;

    },

    addEventListener: function ( type, listener ) {

        if ( this._listeners === undefined ) this._listeners = {};

        var listeners = this._listeners;

        if ( listeners[ type ] === undefined ) {

            listeners[ type ] = [];

        }

        if ( listeners[ type ].indexOf( listener ) === - 1 ) {

            listeners[ type ].push( listener );

        }

    },

    hasEventListener: function ( type, listener ) {

        if ( this._listeners === undefined ) return false;

        var listeners = this._listeners;

        if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {

            return true;

        }

        return false;

    },

    removeEventListener: function ( type, listener ) {

        if ( this._listeners === undefined ) return;

        var listeners = this._listeners;
        var listenerArray = listeners[ type ];

        if ( listenerArray !== undefined ) {

            var index = listenerArray.indexOf( listener );

            if ( index !== - 1 ) {

                listenerArray.splice( index, 1 );

            }

        }

    },

    dispatchEvent: function ( event ) {

        if ( this._listeners === undefined ) return;

        var listeners = this._listeners;
        var listenerArray = listeners[ event.type ];

        if ( listenerArray !== undefined ) {

            event.target = this;

            var array = [];
            var length = listenerArray.length;

            for ( var i = 0; i < length; i ++ ) {

                array[ i ] = listenerArray[ i ];

            }

            for ( var i = 0; i < length; i ++ ) {

                array[ i ].call( this, event );

            }

        }

    }

};

// File:src/core/Raycaster.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author bhouston / http://clara.io/
 * @author stephomi / http://stephaneginier.com/
 */

( function ( THREE ) {

    THREE.Raycaster = function ( origin, direction, near, far ) {

        this.ray = new THREE.Ray( origin, direction );
        // direction is assumed to be normalized (for accurate distance calculations)

        this.near = near || 0;
        this.far = far || Infinity;

        this.params = {
            Mesh: {},
            Line: {},
            LOD: {},
            Points: { threshold: 1 },
            Sprite: {}
        };

        Object.defineProperties( this.params, {
            PointCloud: {
                get: function () {
                    console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' );
                    return this.Points;
                }
            }
        } );

    };

    function descSort( a, b ) {

        return a.distance - b.distance;

    }

    function intersectObject( object, raycaster, intersects, recursive ) {

        if ( object.visible === false ) return;

        object.raycast( raycaster, intersects );

        if ( recursive === true ) {

            var children = object.children;

            for ( var i = 0, l = children.length; i < l; i ++ ) {

                intersectObject( children[ i ], raycaster, intersects, true );

            }

        }

    }

    //

    THREE.Raycaster.prototype = {

        constructor: THREE.Raycaster,

        linePrecision: 1,

        set: function ( origin, direction ) {

            // direction is assumed to be normalized (for accurate distance calculations)

            this.ray.set( origin, direction );

        },

        setFromCamera: function ( coords, camera ) {

            if ( camera instanceof THREE.PerspectiveCamera ) {

                this.ray.origin.setFromMatrixPosition( camera.matrixWorld );
                this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize();

            } else if ( camera instanceof THREE.OrthographicCamera ) {

                this.ray.origin.set( coords.x, coords.y, - 1 ).unproject( camera );
                this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );

            } else {

                console.error( 'THREE.Raycaster: Unsupported camera type.' );

            }

        },

        intersectObject: function ( object, recursive ) {

            var intersects = [];

            intersectObject( object, this, intersects, recursive );

            intersects.sort( descSort );

            return intersects;

        },

        intersectObjects: function ( objects, recursive ) {

            var intersects = [];

            if ( Array.isArray( objects ) === false ) {

                console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' );
                return intersects;

            }

            for ( var i = 0, l = objects.length; i < l; i ++ ) {

                intersectObject( objects[ i ], this, intersects, recursive );

            }

            intersects.sort( descSort );

            return intersects;

        }

    };

}( THREE ) );

// File:src/core/Object3D.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 * @author WestLangley / http://github.com/WestLangley
 * @author elephantatwork / www.elephantatwork.ch
 */

THREE.Object3D = function () {

    Object.defineProperty( this, 'id', { value: THREE.Object3DIdCount ++ } );

    this.uuid = THREE.Math.generateUUID();

    this.name = '';
    this.type = 'Object3D';

    this.parent = null;
    this.channels = new THREE.Channels();
    this.children = [];

    this.up = THREE.Object3D.DefaultUp.clone();

    var position = new THREE.Vector3();
    var rotation = new THREE.Euler();
    var quaternion = new THREE.Quaternion();
    var scale = new THREE.Vector3( 1, 1, 1 );

    function onRotationChange() {

        quaternion.setFromEuler( rotation, false );

    }

    function onQuaternionChange() {

        rotation.setFromQuaternion( quaternion, undefined, false );

    }

    rotation.onChange( onRotationChange );
    quaternion.onChange( onQuaternionChange );

    Object.defineProperties( this, {
        position: {
            enumerable: true,
            value: position
        },
        rotation: {
            enumerable: true,
            value: rotation
        },
        quaternion: {
            enumerable: true,
            value: quaternion
        },
        scale: {
            enumerable: true,
            value: scale
        },
        modelViewMatrix: {
            value: new THREE.Matrix4()
        },
        normalMatrix: {
            value: new THREE.Matrix3()
        }
    } );

    this.rotationAutoUpdate = true;

    this.matrix = new THREE.Matrix4();
    this.matrixWorld = new THREE.Matrix4();

    this.matrixAutoUpdate = THREE.Object3D.DefaultMatrixAutoUpdate;
    this.matrixWorldNeedsUpdate = false;

    this.visible = true;

    this.castShadow = false;
    this.receiveShadow = false;

    this.frustumCulled = true;
    this.renderOrder = 0;

    this.userData = {};

};

THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 1, 0 );
THREE.Object3D.DefaultMatrixAutoUpdate = true;

THREE.Object3D.prototype = {

    constructor: THREE.Object3D,

    get eulerOrder () {

        console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );

        return this.rotation.order;

    },

    set eulerOrder ( value ) {

        console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );

        this.rotation.order = value;

    },

    get useQuaternion () {

        console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );

    },

    set useQuaternion ( value ) {

        console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );

    },

    set renderDepth ( value ) {

        console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' );

    },

    //

    applyMatrix: function ( matrix ) {

        this.matrix.multiplyMatrices( matrix, this.matrix );

        this.matrix.decompose( this.position, this.quaternion, this.scale );

    },

    setRotationFromAxisAngle: function ( axis, angle ) {

        // assumes axis is normalized

        this.quaternion.setFromAxisAngle( axis, angle );

    },

    setRotationFromEuler: function ( euler ) {

        this.quaternion.setFromEuler( euler, true );

    },

    setRotationFromMatrix: function ( m ) {

        // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)

        this.quaternion.setFromRotationMatrix( m );

    },

    setRotationFromQuaternion: function ( q ) {

        // assumes q is normalized

        this.quaternion.copy( q );

    },

    rotateOnAxis: function () {

        // rotate object on axis in object space
        // axis is assumed to be normalized

        var q1 = new THREE.Quaternion();

        return function ( axis, angle ) {

            q1.setFromAxisAngle( axis, angle );

            this.quaternion.multiply( q1 );

            return this;

        };

    }(),

    rotateX: function () {

        var v1 = new THREE.Vector3( 1, 0, 0 );

        return function ( angle ) {

            return this.rotateOnAxis( v1, angle );

        };

    }(),

    rotateY: function () {

        var v1 = new THREE.Vector3( 0, 1, 0 );

        return function ( angle ) {

            return this.rotateOnAxis( v1, angle );

        };

    }(),

    rotateZ: function () {

        var v1 = new THREE.Vector3( 0, 0, 1 );

        return function ( angle ) {

            return this.rotateOnAxis( v1, angle );

        };

    }(),

    translateOnAxis: function () {

        // translate object by distance along axis in object space
        // axis is assumed to be normalized

        var v1 = new THREE.Vector3();

        return function ( axis, distance ) {

            v1.copy( axis ).applyQuaternion( this.quaternion );

            this.position.add( v1.multiplyScalar( distance ) );

            return this;

        };

    }(),

    translate: function ( distance, axis ) {

        console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' );
        return this.translateOnAxis( axis, distance );

    },

    translateX: function () {

        var v1 = new THREE.Vector3( 1, 0, 0 );

        return function ( distance ) {

            return this.translateOnAxis( v1, distance );

        };

    }(),

    translateY: function () {

        var v1 = new THREE.Vector3( 0, 1, 0 );

        return function ( distance ) {

            return this.translateOnAxis( v1, distance );

        };

    }(),

    translateZ: function () {

        var v1 = new THREE.Vector3( 0, 0, 1 );

        return function ( distance ) {

            return this.translateOnAxis( v1, distance );

        };

    }(),

    localToWorld: function ( vector ) {

        return vector.applyMatrix4( this.matrixWorld );

    },

    worldToLocal: function () {

        var m1 = new THREE.Matrix4();

        return function ( vector ) {

            return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) );

        };

    }(),

    lookAt: function () {

        // This routine does not support objects with rotated and/or translated parent(s)

        var m1 = new THREE.Matrix4();

        return function ( vector ) {

            m1.lookAt( vector, this.position, this.up );

            this.quaternion.setFromRotationMatrix( m1 );

        };

    }(),

    add: function ( object ) {

        if ( arguments.length > 1 ) {

            for ( var i = 0; i < arguments.length; i ++ ) {

                this.add( arguments[ i ] );

            }

            return this;

        }

        if ( object === this ) {

            console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object );
            return this;

        }

        if ( object instanceof THREE.Object3D ) {

            if ( object.parent !== null ) {

                object.parent.remove( object );

            }

            object.parent = this;
            object.dispatchEvent( { type: 'added' } );

            this.children.push( object );

        } else {

            console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object );

        }

        return this;

    },

    remove: function ( object ) {

        if ( arguments.length > 1 ) {

            for ( var i = 0; i < arguments.length; i ++ ) {

                this.remove( arguments[ i ] );

            }

        }

        var index = this.children.indexOf( object );

        if ( index !== - 1 ) {

            object.parent = null;

            object.dispatchEvent( { type: 'removed' } );

            this.children.splice( index, 1 );

        }

    },

    getChildByName: function ( name ) {

        console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' );
        return this.getObjectByName( name );

    },

    getObjectById: function ( id ) {

        return this.getObjectByProperty( 'id', id );

    },

    getObjectByName: function ( name ) {

        return this.getObjectByProperty( 'name', name );

    },

    getObjectByProperty: function ( name, value ) {

        if ( this[ name ] === value ) return this;

        for ( var i = 0, l = this.children.length; i < l; i ++ ) {

            var child = this.children[ i ];
            var object = child.getObjectByProperty( name, value );

            if ( object !== undefined ) {

                return object;

            }

        }

        return undefined;

    },

    getWorldPosition: function ( optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();

        this.updateMatrixWorld( true );

        return result.setFromMatrixPosition( this.matrixWorld );

    },

    getWorldQuaternion: function () {

        var position = new THREE.Vector3();
        var scale = new THREE.Vector3();

        return function ( optionalTarget ) {

            var result = optionalTarget || new THREE.Quaternion();

            this.updateMatrixWorld( true );

            this.matrixWorld.decompose( position, result, scale );

            return result;

        };

    }(),

    getWorldRotation: function () {

        var quaternion = new THREE.Quaternion();

        return function ( optionalTarget ) {

            var result = optionalTarget || new THREE.Euler();

            this.getWorldQuaternion( quaternion );

            return result.setFromQuaternion( quaternion, this.rotation.order, false );

        };

    }(),

    getWorldScale: function () {

        var position = new THREE.Vector3();
        var quaternion = new THREE.Quaternion();

        return function ( optionalTarget ) {

            var result = optionalTarget || new THREE.Vector3();

            this.updateMatrixWorld( true );

            this.matrixWorld.decompose( position, quaternion, result );

            return result;

        };

    }(),

    getWorldDirection: function () {

        var quaternion = new THREE.Quaternion();

        return function ( optionalTarget ) {

            var result = optionalTarget || new THREE.Vector3();

            this.getWorldQuaternion( quaternion );

            return result.set( 0, 0, 1 ).applyQuaternion( quaternion );

        };

    }(),

    raycast: function () {},

    traverse: function ( callback ) {

        callback( this );

        var children = this.children;

        for ( var i = 0, l = children.length; i < l; i ++ ) {

            children[ i ].traverse( callback );

        }

    },

    traverseVisible: function ( callback ) {

        if ( this.visible === false ) return;

        callback( this );

        var children = this.children;

        for ( var i = 0, l = children.length; i < l; i ++ ) {

            children[ i ].traverseVisible( callback );

        }

    },

    traverseAncestors: function ( callback ) {

        var parent = this.parent;

        if ( parent !== null ) {

            callback( parent );

            parent.traverseAncestors( callback );

        }

    },

    updateMatrix: function () {

        this.matrix.compose( this.position, this.quaternion, this.scale );

        this.matrixWorldNeedsUpdate = true;

    },

    updateMatrixWorld: function ( force ) {

        if ( this.matrixAutoUpdate === true ) this.updateMatrix();

        if ( this.matrixWorldNeedsUpdate === true || force === true ) {

            if ( this.parent === null ) {

                this.matrixWorld.copy( this.matrix );

            } else {

                this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );

            }

            this.matrixWorldNeedsUpdate = false;

            force = true;

        }

        // update children

        for ( var i = 0, l = this.children.length; i < l; i ++ ) {

            this.children[ i ].updateMatrixWorld( force );

        }

    },

    toJSON: function ( meta ) {

        var isRootObject = ( meta === undefined );

        var output = {};

        // meta is a hash used to collect geometries, materials.
        // not providing it implies that this is the root object
        // being serialized.
        if ( isRootObject ) {

            // initialize meta obj
            meta = {
                geometries: {},
                materials: {},
                textures: {},
                images: {}
            };

            output.metadata = {
                version: 4.4,
                type: 'Object',
                generator: 'Object3D.toJSON'
            };

        }

        // standard Object3D serialization

        var object = {};

        object.uuid = this.uuid;
        object.type = this.type;

        if ( this.name !== '' ) object.name = this.name;
        if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
        if ( this.castShadow === true ) object.castShadow = true;
        if ( this.receiveShadow === true ) object.receiveShadow = true;
        if ( this.visible === false ) object.visible = false;

        object.matrix = this.matrix.toArray();

        //

        if ( this.geometry !== undefined ) {

            if ( meta.geometries[ this.geometry.uuid ] === undefined ) {

                meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta );

            }

            object.geometry = this.geometry.uuid;

        }

        if ( this.material !== undefined ) {

            if ( meta.materials[ this.material.uuid ] === undefined ) {

                meta.materials[ this.material.uuid ] = this.material.toJSON( meta );

            }

            object.material = this.material.uuid;

        }

        //

        if ( this.children.length > 0 ) {

            object.children = [];

            for ( var i = 0; i < this.children.length; i ++ ) {

                object.children.push( this.children[ i ].toJSON( meta ).object );

            }

        }

        if ( isRootObject ) {

            var geometries = extractFromCache( meta.geometries );
            var materials = extractFromCache( meta.materials );
            var textures = extractFromCache( meta.textures );
            var images = extractFromCache( meta.images );

            if ( geometries.length > 0 ) output.geometries = geometries;
            if ( materials.length > 0 ) output.materials = materials;
            if ( textures.length > 0 ) output.textures = textures;
            if ( images.length > 0 ) output.images = images;

        }

        output.object = object;

        return output;

        // extract data from the cache hash
        // remove metadata on each item
        // and return as array
        function extractFromCache ( cache ) {

            var values = [];
            for ( var key in cache ) {

                var data = cache[ key ];
                delete data.metadata;
                values.push( data );

            }
            return values;

        }

    },

    clone: function ( recursive ) {

        return new this.constructor().copy( this, recursive );

    },

    copy: function ( source, recursive ) {

        if ( recursive === undefined ) recursive = true;

        this.name = source.name;

        this.up.copy( source.up );

        this.position.copy( source.position );
        this.quaternion.copy( source.quaternion );
        this.scale.copy( source.scale );

        this.rotationAutoUpdate = source.rotationAutoUpdate;

        this.matrix.copy( source.matrix );
        this.matrixWorld.copy( source.matrixWorld );

        this.matrixAutoUpdate = source.matrixAutoUpdate;
        this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;

        this.visible = source.visible;

        this.castShadow = source.castShadow;
        this.receiveShadow = source.receiveShadow;

        this.frustumCulled = source.frustumCulled;
        this.renderOrder = source.renderOrder;

        this.userData = JSON.parse( JSON.stringify( source.userData ) );

        if ( recursive === true ) {

            for ( var i = 0; i < source.children.length; i ++ ) {

                var child = source.children[ i ];
                this.add( child.clone() );

            }

        }

        return this;

    }

};

THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype );

THREE.Object3DIdCount = 0;

// File:src/core/Face3.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) {

    this.a = a;
    this.b = b;
    this.c = c;

    this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3();
    this.vertexNormals = Array.isArray( normal ) ? normal : [];

    this.color = color instanceof THREE.Color ? color : new THREE.Color();
    this.vertexColors = Array.isArray( color ) ? color : [];

    this.materialIndex = materialIndex !== undefined ? materialIndex : 0;

};

THREE.Face3.prototype = {

    constructor: THREE.Face3,

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( source ) {

        this.a = source.a;
        this.b = source.b;
        this.c = source.c;

        this.normal.copy( source.normal );
        this.color.copy( source.color );

        this.materialIndex = source.materialIndex;

        for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) {

            this.vertexNormals[ i ] = source.vertexNormals[ i ].clone();

        }

        for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) {

            this.vertexColors[ i ] = source.vertexColors[ i ].clone();

        }

        return this;

    }

};

// File:src/core/Face4.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) {

    console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' );
    return new THREE.Face3( a, b, c, normal, color, materialIndex );

};

// File:src/core/BufferAttribute.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.BufferAttribute = function ( array, itemSize ) {

    this.uuid = THREE.Math.generateUUID();

    this.array = array;
    this.itemSize = itemSize;

    this.dynamic = false;
    this.updateRange = { offset: 0, count: - 1 };

    this.version = 0;

};

THREE.BufferAttribute.prototype = {

    constructor: THREE.BufferAttribute,

    get length() {

        console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' );
        return this.array.length;

    },

    get count() {

        return this.array.length / this.itemSize;

    },

    set needsUpdate( value ) {

        if ( value === true ) this.version ++;

    },

    setDynamic: function ( value ) {

        this.dynamic = value;

        return this;

    },

    copy: function ( source ) {

        this.array = new source.array.constructor( source.array );
        this.itemSize = source.itemSize;

        this.dynamic = source.dynamic;

        return this;

    },

    copyAt: function ( index1, attribute, index2 ) {

        index1 *= this.itemSize;
        index2 *= attribute.itemSize;

        for ( var i = 0, l = this.itemSize; i < l; i ++ ) {

            this.array[ index1 + i ] = attribute.array[ index2 + i ];

        }

        return this;

    },

    copyArray: function ( array ) {

        this.array.set( array );

        return this;

    },

    copyColorsArray: function ( colors ) {

        var array = this.array, offset = 0;

        for ( var i = 0, l = colors.length; i < l; i ++ ) {

            var color = colors[ i ];

            if ( color === undefined ) {

                console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i );
                color = new THREE.Color();

            }

            array[ offset ++ ] = color.r;
            array[ offset ++ ] = color.g;
            array[ offset ++ ] = color.b;

        }

        return this;

    },

    copyIndicesArray: function ( indices ) {

        var array = this.array, offset = 0;

        for ( var i = 0, l = indices.length; i < l; i ++ ) {

            var index = indices[ i ];

            array[ offset ++ ] = index.a;
            array[ offset ++ ] = index.b;
            array[ offset ++ ] = index.c;

        }

        return this;

    },

    copyVector2sArray: function ( vectors ) {

        var array = this.array, offset = 0;

        for ( var i = 0, l = vectors.length; i < l; i ++ ) {

            var vector = vectors[ i ];

            if ( vector === undefined ) {

                console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i );
                vector = new THREE.Vector2();

            }

            array[ offset ++ ] = vector.x;
            array[ offset ++ ] = vector.y;

        }

        return this;

    },

    copyVector3sArray: function ( vectors ) {

        var array = this.array, offset = 0;

        for ( var i = 0, l = vectors.length; i < l; i ++ ) {

            var vector = vectors[ i ];

            if ( vector === undefined ) {

                console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i );
                vector = new THREE.Vector3();

            }

            array[ offset ++ ] = vector.x;
            array[ offset ++ ] = vector.y;
            array[ offset ++ ] = vector.z;

        }

        return this;

    },

    copyVector4sArray: function ( vectors ) {

        var array = this.array, offset = 0;

        for ( var i = 0, l = vectors.length; i < l; i ++ ) {

            var vector = vectors[ i ];

            if ( vector === undefined ) {

                console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i );
                vector = new THREE.Vector4();

            }

            array[ offset ++ ] = vector.x;
            array[ offset ++ ] = vector.y;
            array[ offset ++ ] = vector.z;
            array[ offset ++ ] = vector.w;

        }

        return this;

    },

    set: function ( value, offset ) {

        if ( offset === undefined ) offset = 0;

        this.array.set( value, offset );

        return this;

    },

    getX: function ( index ) {

        return this.array[ index * this.itemSize ];

    },

    setX: function ( index, x ) {

        this.array[ index * this.itemSize ] = x;

        return this;

    },

    getY: function ( index ) {

        return this.array[ index * this.itemSize + 1 ];

    },

    setY: function ( index, y ) {

        this.array[ index * this.itemSize + 1 ] = y;

        return this;

    },

    getZ: function ( index ) {

        return this.array[ index * this.itemSize + 2 ];

    },

    setZ: function ( index, z ) {

        this.array[ index * this.itemSize + 2 ] = z;

        return this;

    },

    getW: function ( index ) {

        return this.array[ index * this.itemSize + 3 ];

    },

    setW: function ( index, w ) {

        this.array[ index * this.itemSize + 3 ] = w;

        return this;

    },

    setXY: function ( index, x, y ) {

        index *= this.itemSize;

        this.array[ index + 0 ] = x;
        this.array[ index + 1 ] = y;

        return this;

    },

    setXYZ: function ( index, x, y, z ) {

        index *= this.itemSize;

        this.array[ index + 0 ] = x;
        this.array[ index + 1 ] = y;
        this.array[ index + 2 ] = z;

        return this;

    },

    setXYZW: function ( index, x, y, z, w ) {

        index *= this.itemSize;

        this.array[ index + 0 ] = x;
        this.array[ index + 1 ] = y;
        this.array[ index + 2 ] = z;
        this.array[ index + 3 ] = w;

        return this;

    },

    clone: function () {

        return new this.constructor().copy( this );

    }

};

//

THREE.Int8Attribute = function ( array, itemSize ) {

    return new THREE.BufferAttribute( new Int8Array( array ), itemSize );

};

THREE.Uint8Attribute = function ( array, itemSize ) {

    return new THREE.BufferAttribute( new Uint8Array( array ), itemSize );

};

THREE.Uint8ClampedAttribute = function ( array, itemSize ) {

    return new THREE.BufferAttribute( new Uint8ClampedArray( array ), itemSize );

};

THREE.Int16Attribute = function ( array, itemSize ) {

    return new THREE.BufferAttribute( new Int16Array( array ), itemSize );

};

THREE.Uint16Attribute = function ( array, itemSize ) {

    return new THREE.BufferAttribute( new Uint16Array( array ), itemSize );

};

THREE.Int32Attribute = function ( array, itemSize ) {

    return new THREE.BufferAttribute( new Int32Array( array ), itemSize );

};

THREE.Uint32Attribute = function ( array, itemSize ) {

    return new THREE.BufferAttribute( new Uint32Array( array ), itemSize );

};

THREE.Float32Attribute = function ( array, itemSize ) {

    return new THREE.BufferAttribute( new Float32Array( array ), itemSize );

};

THREE.Float64Attribute = function ( array, itemSize ) {

    return new THREE.BufferAttribute( new Float64Array( array ), itemSize );

};


// Deprecated

THREE.DynamicBufferAttribute = function ( array, itemSize ) {

    console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' );
    return new THREE.BufferAttribute( array, itemSize ).setDynamic( true );

};

// File:src/core/InstancedBufferAttribute.js

/**
 * @author benaadams / https://twitter.com/ben_a_adams
 */

THREE.InstancedBufferAttribute = function ( array, itemSize, meshPerAttribute ) {

    THREE.BufferAttribute.call( this, array, itemSize );

    this.meshPerAttribute = meshPerAttribute || 1;

};

THREE.InstancedBufferAttribute.prototype = Object.create( THREE.BufferAttribute.prototype );
THREE.InstancedBufferAttribute.prototype.constructor = THREE.InstancedBufferAttribute;

THREE.InstancedBufferAttribute.prototype.copy = function ( source ) {

    THREE.BufferAttribute.prototype.copy.call( this, source );

    this.meshPerAttribute = source.meshPerAttribute;

    return this;

};

// File:src/core/InterleavedBuffer.js

/**
 * @author benaadams / https://twitter.com/ben_a_adams
 */

THREE.InterleavedBuffer = function ( array, stride ) {

    this.uuid = THREE.Math.generateUUID();

    this.array = array;
    this.stride = stride;

    this.dynamic = false;
    this.updateRange = { offset: 0, count: - 1 };

    this.version = 0;

};

THREE.InterleavedBuffer.prototype = {

    constructor: THREE.InterleavedBuffer,

    get length () {

        return this.array.length;

    },

    get count () {

        return this.array.length / this.stride;

    },

    set needsUpdate( value ) {

        if ( value === true ) this.version ++;

    },

    setDynamic: function ( value ) {

        this.dynamic = value;

        return this;

    },

    copy: function ( source ) {

        this.array = new source.array.constructor( source.array );
        this.stride = source.stride;
        this.dynamic = source.dynamic;

    },

    copyAt: function ( index1, attribute, index2 ) {

        index1 *= this.stride;
        index2 *= attribute.stride;

        for ( var i = 0, l = this.stride; i < l; i ++ ) {

            this.array[ index1 + i ] = attribute.array[ index2 + i ];

        }

        return this;

    },

    set: function ( value, offset ) {

        if ( offset === undefined ) offset = 0;

        this.array.set( value, offset );

        return this;

    },

    clone: function () {

        return new this.constructor().copy( this );

    }

};

// File:src/core/InstancedInterleavedBuffer.js

/**
 * @author benaadams / https://twitter.com/ben_a_adams
 */

THREE.InstancedInterleavedBuffer = function ( array, stride, meshPerAttribute ) {

    THREE.InterleavedBuffer.call( this, array, stride );

    this.meshPerAttribute = meshPerAttribute || 1;

};

THREE.InstancedInterleavedBuffer.prototype = Object.create( THREE.InterleavedBuffer.prototype );
THREE.InstancedInterleavedBuffer.prototype.constructor = THREE.InstancedInterleavedBuffer;

THREE.InstancedInterleavedBuffer.prototype.copy = function ( source ) {

    THREE.InterleavedBuffer.prototype.copy.call( this, source );

    this.meshPerAttribute = source.meshPerAttribute;

    return this;

};

// File:src/core/InterleavedBufferAttribute.js

/**
 * @author benaadams / https://twitter.com/ben_a_adams
 */

THREE.InterleavedBufferAttribute = function ( interleavedBuffer, itemSize, offset ) {

    this.uuid = THREE.Math.generateUUID();

    this.data = interleavedBuffer;
    this.itemSize = itemSize;
    this.offset = offset;

};


THREE.InterleavedBufferAttribute.prototype = {

    constructor: THREE.InterleavedBufferAttribute,

    get length() {

        console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' );
        return this.array.length;

    },

    get count() {

        return this.data.array.length / this.data.stride;

    },

    setX: function ( index, x ) {

        this.data.array[ index * this.data.stride + this.offset ] = x;

        return this;

    },

    setY: function ( index, y ) {

        this.data.array[ index * this.data.stride + this.offset + 1 ] = y;

        return this;

    },

    setZ: function ( index, z ) {

        this.data.array[ index * this.data.stride + this.offset + 2 ] = z;

        return this;

    },

    setW: function ( index, w ) {

        this.data.array[ index * this.data.stride + this.offset + 3 ] = w;

        return this;

    },

    getX: function ( index ) {

        return this.data.array[ index * this.data.stride + this.offset ];

    },

    getY: function ( index ) {

        return this.data.array[ index * this.data.stride + this.offset + 1 ];

    },

    getZ: function ( index ) {

        return this.data.array[ index * this.data.stride + this.offset + 2 ];

    },

    getW: function ( index ) {

        return this.data.array[ index * this.data.stride + this.offset + 3 ];

    },

    setXY: function ( index, x, y ) {

        index = index * this.data.stride + this.offset;

        this.data.array[ index + 0 ] = x;
        this.data.array[ index + 1 ] = y;

        return this;

    },

    setXYZ: function ( index, x, y, z ) {

        index = index * this.data.stride + this.offset;

        this.data.array[ index + 0 ] = x;
        this.data.array[ index + 1 ] = y;
        this.data.array[ index + 2 ] = z;

        return this;

    },

    setXYZW: function ( index, x, y, z, w ) {

        index = index * this.data.stride + this.offset;

        this.data.array[ index + 0 ] = x;
        this.data.array[ index + 1 ] = y;
        this.data.array[ index + 2 ] = z;
        this.data.array[ index + 3 ] = w;

        return this;

    }

};

// File:src/core/Geometry.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author kile / http://kile.stravaganza.org/
 * @author alteredq / http://alteredqualia.com/
 * @author mikael emtinger / http://gomo.se/
 * @author zz85 / http://www.lab4games.net/zz85/blog
 * @author bhouston / http://clara.io
 */

THREE.Geometry = function () {

    Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );

    this.uuid = THREE.Math.generateUUID();

    this.name = '';
    this.type = 'Geometry';

    this.vertices = [];
    this.colors = [];
    this.faces = [];
    this.faceVertexUvs = [ [] ];

    this.morphTargets = [];
    this.morphNormals = [];

    this.skinWeights = [];
    this.skinIndices = [];

    this.lineDistances = [];

    this.boundingBox = null;
    this.boundingSphere = null;

    // update flags

    this.verticesNeedUpdate = false;
    this.elementsNeedUpdate = false;
    this.uvsNeedUpdate = false;
    this.normalsNeedUpdate = false;
    this.colorsNeedUpdate = false;
    this.lineDistancesNeedUpdate = false;
    this.groupsNeedUpdate = false;

};

THREE.Geometry.prototype = {

    constructor: THREE.Geometry,

    applyMatrix: function ( matrix ) {

        var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );

        for ( var i = 0, il = this.vertices.length; i < il; i ++ ) {

            var vertex = this.vertices[ i ];
            vertex.applyMatrix4( matrix );

        }

        for ( var i = 0, il = this.faces.length; i < il; i ++ ) {

            var face = this.faces[ i ];
            face.normal.applyMatrix3( normalMatrix ).normalize();

            for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {

                face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();

            }

        }

        if ( this.boundingBox !== null ) {

            this.computeBoundingBox();

        }

        if ( this.boundingSphere !== null ) {

            this.computeBoundingSphere();

        }

        this.verticesNeedUpdate = true;
        this.normalsNeedUpdate = true;

    },

    rotateX: function () {

        // rotate geometry around world x-axis

        var m1;

        return function rotateX( angle ) {

            if ( m1 === undefined ) m1 = new THREE.Matrix4();

            m1.makeRotationX( angle );

            this.applyMatrix( m1 );

            return this;

        };

    }(),

    rotateY: function () {

        // rotate geometry around world y-axis

        var m1;

        return function rotateY( angle ) {

            if ( m1 === undefined ) m1 = new THREE.Matrix4();

            m1.makeRotationY( angle );

            this.applyMatrix( m1 );

            return this;

        };

    }(),

    rotateZ: function () {

        // rotate geometry around world z-axis

        var m1;

        return function rotateZ( angle ) {

            if ( m1 === undefined ) m1 = new THREE.Matrix4();

            m1.makeRotationZ( angle );

            this.applyMatrix( m1 );

            return this;

        };

    }(),

    translate: function () {

        // translate geometry

        var m1;

        return function translate( x, y, z ) {

            if ( m1 === undefined ) m1 = new THREE.Matrix4();

            m1.makeTranslation( x, y, z );

            this.applyMatrix( m1 );

            return this;

        };

    }(),

    scale: function () {

        // scale geometry

        var m1;

        return function scale( x, y, z ) {

            if ( m1 === undefined ) m1 = new THREE.Matrix4();

            m1.makeScale( x, y, z );

            this.applyMatrix( m1 );

            return this;

        };

    }(),

    lookAt: function () {

        var obj;

        return function lookAt( vector ) {

            if ( obj === undefined ) obj = new THREE.Object3D();

            obj.lookAt( vector );

            obj.updateMatrix();

            this.applyMatrix( obj.matrix );

        };

    }(),

    fromBufferGeometry: function ( geometry ) {

        var scope = this;

        var indices = geometry.index !== null ? geometry.index.array : undefined;
        var attributes = geometry.attributes;

        var vertices = attributes.position.array;
        var normals = attributes.normal !== undefined ? attributes.normal.array : undefined;
        var colors = attributes.color !== undefined ? attributes.color.array : undefined;
        var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined;
        var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined;

        if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = [];

        var tempNormals = [];
        var tempUVs = [];
        var tempUVs2 = [];

        for ( var i = 0, j = 0, k = 0; i < vertices.length; i += 3, j += 2, k += 4 ) {

            scope.vertices.push( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );

            if ( normals !== undefined ) {

                tempNormals.push( new THREE.Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) );

            }

            if ( colors !== undefined ) {

                scope.colors.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );

            }

            if ( uvs !== undefined ) {

                tempUVs.push( new THREE.Vector2( uvs[ j ], uvs[ j + 1 ] ) );

            }

            if ( uvs2 !== undefined ) {

                tempUVs2.push( new THREE.Vector2( uvs2[ j ], uvs2[ j + 1 ] ) );

            }

        }

        function addFace( a, b, c ) {

            var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : [];
            var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : [];

            var face = new THREE.Face3( a, b, c, vertexNormals, vertexColors );

            scope.faces.push( face );

            if ( uvs !== undefined ) {

                scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] );

            }

            if ( uvs2 !== undefined ) {

                scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] );

            }

        };

        if ( indices !== undefined ) {

            var groups = geometry.groups;

            if ( groups.length > 0 ) {

                for ( var i = 0; i < groups.length; i ++ ) {

                    var group = groups[ i ];

                    var start = group.start;
                    var count = group.count;

                    for ( var j = start, jl = start + count; j < jl; j += 3 ) {

                        addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ] );

                    }

                }

            } else {

                for ( var i = 0; i < indices.length; i += 3 ) {

                    addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );

                }

            }

        } else {

            for ( var i = 0; i < vertices.length / 3; i += 3 ) {

                addFace( i, i + 1, i + 2 );

            }

        }

        this.computeFaceNormals();

        if ( geometry.boundingBox !== null ) {

            this.boundingBox = geometry.boundingBox.clone();

        }

        if ( geometry.boundingSphere !== null ) {

            this.boundingSphere = geometry.boundingSphere.clone();

        }

        return this;

    },

    center: function () {

        this.computeBoundingBox();

        var offset = this.boundingBox.center().negate();

        this.translate( offset.x, offset.y, offset.z );

        return offset;

    },

    normalize: function () {

        this.computeBoundingSphere();

        var center = this.boundingSphere.center;
        var radius = this.boundingSphere.radius;

        var s = radius === 0 ? 1 : 1.0 / radius;

        var matrix = new THREE.Matrix4();
        matrix.set(
            s, 0, 0, - s * center.x,
            0, s, 0, - s * center.y,
            0, 0, s, - s * center.z,
            0, 0, 0, 1
        );

        this.applyMatrix( matrix );

        return this;

    },

    computeFaceNormals: function () {

        var cb = new THREE.Vector3(), ab = new THREE.Vector3();

        for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) {

            var face = this.faces[ f ];

            var vA = this.vertices[ face.a ];
            var vB = this.vertices[ face.b ];
            var vC = this.vertices[ face.c ];

            cb.subVectors( vC, vB );
            ab.subVectors( vA, vB );
            cb.cross( ab );

            cb.normalize();

            face.normal.copy( cb );

        }

    },

    computeVertexNormals: function ( areaWeighted ) {

        var v, vl, f, fl, face, vertices;

        vertices = new Array( this.vertices.length );

        for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {

            vertices[ v ] = new THREE.Vector3();

        }

        if ( areaWeighted ) {

            // vertex normals weighted by triangle areas
            // http://www.iquilezles.org/www/articles/normals/normals.htm

            var vA, vB, vC;
            var cb = new THREE.Vector3(), ab = new THREE.Vector3();

            for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {

                face = this.faces[ f ];

                vA = this.vertices[ face.a ];
                vB = this.vertices[ face.b ];
                vC = this.vertices[ face.c ];

                cb.subVectors( vC, vB );
                ab.subVectors( vA, vB );
                cb.cross( ab );

                vertices[ face.a ].add( cb );
                vertices[ face.b ].add( cb );
                vertices[ face.c ].add( cb );

            }

        } else {

            for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {

                face = this.faces[ f ];

                vertices[ face.a ].add( face.normal );
                vertices[ face.b ].add( face.normal );
                vertices[ face.c ].add( face.normal );

            }

        }

        for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {

            vertices[ v ].normalize();

        }

        for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {

            face = this.faces[ f ];

            var vertexNormals = face.vertexNormals;

            if ( vertexNormals.length === 3 ) {

                vertexNormals[ 0 ].copy( vertices[ face.a ] );
                vertexNormals[ 1 ].copy( vertices[ face.b ] );
                vertexNormals[ 2 ].copy( vertices[ face.c ] );

            } else {

                vertexNormals[ 0 ] = vertices[ face.a ].clone();
                vertexNormals[ 1 ] = vertices[ face.b ].clone();
                vertexNormals[ 2 ] = vertices[ face.c ].clone();

            }

        }

    },

    computeMorphNormals: function () {

        var i, il, f, fl, face;

        // save original normals
        // - create temp variables on first access
        //   otherwise just copy (for faster repeated calls)

        for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {

            face = this.faces[ f ];

            if ( ! face.__originalFaceNormal ) {

                face.__originalFaceNormal = face.normal.clone();

            } else {

                face.__originalFaceNormal.copy( face.normal );

            }

            if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];

            for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) {

                if ( ! face.__originalVertexNormals[ i ] ) {

                    face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();

                } else {

                    face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );

                }

            }

        }

        // use temp geometry to compute face and vertex normals for each morph

        var tmpGeo = new THREE.Geometry();
        tmpGeo.faces = this.faces;

        for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {

            // create on first access

            if ( ! this.morphNormals[ i ] ) {

                this.morphNormals[ i ] = {};
                this.morphNormals[ i ].faceNormals = [];
                this.morphNormals[ i ].vertexNormals = [];

                var dstNormalsFace = this.morphNormals[ i ].faceNormals;
                var dstNormalsVertex = this.morphNormals[ i ].vertexNormals;

                var faceNormal, vertexNormals;

                for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {

                    faceNormal = new THREE.Vector3();
                    vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() };

                    dstNormalsFace.push( faceNormal );
                    dstNormalsVertex.push( vertexNormals );

                }

            }

            var morphNormals = this.morphNormals[ i ];

            // set vertices to morph target

            tmpGeo.vertices = this.morphTargets[ i ].vertices;

            // compute morph normals

            tmpGeo.computeFaceNormals();
            tmpGeo.computeVertexNormals();

            // store morph normals

            var faceNormal, vertexNormals;

            for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {

                face = this.faces[ f ];

                faceNormal = morphNormals.faceNormals[ f ];
                vertexNormals = morphNormals.vertexNormals[ f ];

                faceNormal.copy( face.normal );

                vertexNormals.a.copy( face.vertexNormals[ 0 ] );
                vertexNormals.b.copy( face.vertexNormals[ 1 ] );
                vertexNormals.c.copy( face.vertexNormals[ 2 ] );

            }

        }

        // restore original normals

        for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {

            face = this.faces[ f ];

            face.normal = face.__originalFaceNormal;
            face.vertexNormals = face.__originalVertexNormals;

        }

    },

    computeTangents: function () {

        console.warn( 'THREE.Geometry: .computeTangents() has been removed.' );

    },

    computeLineDistances: function () {

        var d = 0;
        var vertices = this.vertices;

        for ( var i = 0, il = vertices.length; i < il; i ++ ) {

            if ( i > 0 ) {

                d += vertices[ i ].distanceTo( vertices[ i - 1 ] );

            }

            this.lineDistances[ i ] = d;

        }

    },

    computeBoundingBox: function () {

        if ( this.boundingBox === null ) {

            this.boundingBox = new THREE.Box3();

        }

        this.boundingBox.setFromPoints( this.vertices );

    },

    computeBoundingSphere: function () {

        if ( this.boundingSphere === null ) {

            this.boundingSphere = new THREE.Sphere();

        }

        this.boundingSphere.setFromPoints( this.vertices );

    },

    merge: function ( geometry, matrix, materialIndexOffset ) {

        if ( geometry instanceof THREE.Geometry === false ) {

            console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry );
            return;

        }

        var normalMatrix,
            vertexOffset = this.vertices.length,
            vertices1 = this.vertices,
            vertices2 = geometry.vertices,
            faces1 = this.faces,
            faces2 = geometry.faces,
            uvs1 = this.faceVertexUvs[ 0 ],
            uvs2 = geometry.faceVertexUvs[ 0 ];

        if ( materialIndexOffset === undefined ) materialIndexOffset = 0;

        if ( matrix !== undefined ) {

            normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );

        }

        // vertices

        for ( var i = 0, il = vertices2.length; i < il; i ++ ) {

            var vertex = vertices2[ i ];

            var vertexCopy = vertex.clone();

            if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix );

            vertices1.push( vertexCopy );

        }

        // faces

        for ( i = 0, il = faces2.length; i < il; i ++ ) {

            var face = faces2[ i ], faceCopy, normal, color,
                faceVertexNormals = face.vertexNormals,
                faceVertexColors = face.vertexColors;

            faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );
            faceCopy.normal.copy( face.normal );

            if ( normalMatrix !== undefined ) {

                faceCopy.normal.applyMatrix3( normalMatrix ).normalize();

            }

            for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {

                normal = faceVertexNormals[ j ].clone();

                if ( normalMatrix !== undefined ) {

                    normal.applyMatrix3( normalMatrix ).normalize();

                }

                faceCopy.vertexNormals.push( normal );

            }

            faceCopy.color.copy( face.color );

            for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {

                color = faceVertexColors[ j ];
                faceCopy.vertexColors.push( color.clone() );

            }

            faceCopy.materialIndex = face.materialIndex + materialIndexOffset;

            faces1.push( faceCopy );

        }

        // uvs

        for ( i = 0, il = uvs2.length; i < il; i ++ ) {

            var uv = uvs2[ i ], uvCopy = [];

            if ( uv === undefined ) {

                continue;

            }

            for ( var j = 0, jl = uv.length; j < jl; j ++ ) {

                uvCopy.push( uv[ j ].clone() );

            }

            uvs1.push( uvCopy );

        }

    },

    mergeMesh: function ( mesh ) {

        if ( mesh instanceof THREE.Mesh === false ) {

            console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh );
            return;

        }

        mesh.matrixAutoUpdate && mesh.updateMatrix();

        this.merge( mesh.geometry, mesh.matrix );

    },

    /*
	 * Checks for duplicate vertices with hashmap.
	 * Duplicated vertices are removed
	 * and faces' vertices are updated.
	 */

    mergeVertices: function () {

        var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
        var unique = [], changes = [];

        var v, key;
        var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001
        var precision = Math.pow( 10, precisionPoints );
        var i, il, face;
        var indices, j, jl;

        for ( i = 0, il = this.vertices.length; i < il; i ++ ) {

            v = this.vertices[ i ];
            key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision );

            if ( verticesMap[ key ] === undefined ) {

                verticesMap[ key ] = i;
                unique.push( this.vertices[ i ] );
                changes[ i ] = unique.length - 1;

            } else {

                //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);
                changes[ i ] = changes[ verticesMap[ key ] ];

            }

        }


        // if faces are completely degenerate after merging vertices, we
        // have to remove them from the geometry.
        var faceIndicesToRemove = [];

        for ( i = 0, il = this.faces.length; i < il; i ++ ) {

            face = this.faces[ i ];

            face.a = changes[ face.a ];
            face.b = changes[ face.b ];
            face.c = changes[ face.c ];

            indices = [ face.a, face.b, face.c ];

            var dupIndex = - 1;

            // if any duplicate vertices are found in a Face3
            // we have to remove the face as nothing can be saved
            for ( var n = 0; n < 3; n ++ ) {

                if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) {

                    dupIndex = n;
                    faceIndicesToRemove.push( i );
                    break;

                }

            }

        }

        for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {

            var idx = faceIndicesToRemove[ i ];

            this.faces.splice( idx, 1 );

            for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {

                this.faceVertexUvs[ j ].splice( idx, 1 );

            }

        }

        // Use unique set of vertices

        var diff = this.vertices.length - unique.length;
        this.vertices = unique;
        return diff;

    },

    sortFacesByMaterialIndex: function () {

        var faces = this.faces;
        var length = faces.length;

        // tag faces

        for ( var i = 0; i < length; i ++ ) {

            faces[ i ]._id = i;

        }

        // sort faces

        function materialIndexSort( a, b ) {

            return a.materialIndex - b.materialIndex;

        }

        faces.sort( materialIndexSort );

        // sort uvs

        var uvs1 = this.faceVertexUvs[ 0 ];
        var uvs2 = this.faceVertexUvs[ 1 ];

        var newUvs1, newUvs2;

        if ( uvs1 && uvs1.length === length ) newUvs1 = [];
        if ( uvs2 && uvs2.length === length ) newUvs2 = [];

        for ( var i = 0; i < length; i ++ ) {

            var id = faces[ i ]._id;

            if ( newUvs1 ) newUvs1.push( uvs1[ id ] );
            if ( newUvs2 ) newUvs2.push( uvs2[ id ] );

        }

        if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1;
        if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2;

    },

    toJSON: function () {

        var data = {
            metadata: {
                version: 4.4,
                type: 'Geometry',
                generator: 'Geometry.toJSON'
            }
        };

        // standard Geometry serialization

        data.uuid = this.uuid;
        data.type = this.type;
        if ( this.name !== '' ) data.name = this.name;

        if ( this.parameters !== undefined ) {

            var parameters = this.parameters;

            for ( var key in parameters ) {

                if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];

            }

            return data;

        }

        var vertices = [];

        for ( var i = 0; i < this.vertices.length; i ++ ) {

            var vertex = this.vertices[ i ];
            vertices.push( vertex.x, vertex.y, vertex.z );

        }

        var faces = [];
        var normals = [];
        var normalsHash = {};
        var colors = [];
        var colorsHash = {};
        var uvs = [];
        var uvsHash = {};

        for ( var i = 0; i < this.faces.length; i ++ ) {

            var face = this.faces[ i ];

            var hasMaterial = false; // face.materialIndex !== undefined;
            var hasFaceUv = false; // deprecated
            var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined;
            var hasFaceNormal = face.normal.length() > 0;
            var hasFaceVertexNormal = face.vertexNormals.length > 0;
            var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1;
            var hasFaceVertexColor = face.vertexColors.length > 0;

            var faceType = 0;

            faceType = setBit( faceType, 0, 0 );
            faceType = setBit( faceType, 1, hasMaterial );
            faceType = setBit( faceType, 2, hasFaceUv );
            faceType = setBit( faceType, 3, hasFaceVertexUv );
            faceType = setBit( faceType, 4, hasFaceNormal );
            faceType = setBit( faceType, 5, hasFaceVertexNormal );
            faceType = setBit( faceType, 6, hasFaceColor );
            faceType = setBit( faceType, 7, hasFaceVertexColor );

            faces.push( faceType );
            faces.push( face.a, face.b, face.c );

            if ( hasFaceVertexUv ) {

                var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ];

                faces.push(
                    getUvIndex( faceVertexUvs[ 0 ] ),
                    getUvIndex( faceVertexUvs[ 1 ] ),
                    getUvIndex( faceVertexUvs[ 2 ] )
                );

            }

            if ( hasFaceNormal ) {

                faces.push( getNormalIndex( face.normal ) );

            }

            if ( hasFaceVertexNormal ) {

                var vertexNormals = face.vertexNormals;

                faces.push(
                    getNormalIndex( vertexNormals[ 0 ] ),
                    getNormalIndex( vertexNormals[ 1 ] ),
                    getNormalIndex( vertexNormals[ 2 ] )
                );

            }

            if ( hasFaceColor ) {

                faces.push( getColorIndex( face.color ) );

            }

            if ( hasFaceVertexColor ) {

                var vertexColors = face.vertexColors;

                faces.push(
                    getColorIndex( vertexColors[ 0 ] ),
                    getColorIndex( vertexColors[ 1 ] ),
                    getColorIndex( vertexColors[ 2 ] )
                );

            }

        }

        function setBit( value, position, enabled ) {

            return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) );

        }

        function getNormalIndex( normal ) {

            var hash = normal.x.toString() + normal.y.toString() + normal.z.toString();

            if ( normalsHash[ hash ] !== undefined ) {

                return normalsHash[ hash ];

            }

            normalsHash[ hash ] = normals.length / 3;
            normals.push( normal.x, normal.y, normal.z );

            return normalsHash[ hash ];

        }

        function getColorIndex( color ) {

            var hash = color.r.toString() + color.g.toString() + color.b.toString();

            if ( colorsHash[ hash ] !== undefined ) {

                return colorsHash[ hash ];

            }

            colorsHash[ hash ] = colors.length;
            colors.push( color.getHex() );

            return colorsHash[ hash ];

        }

        function getUvIndex( uv ) {

            var hash = uv.x.toString() + uv.y.toString();

            if ( uvsHash[ hash ] !== undefined ) {

                return uvsHash[ hash ];

            }

            uvsHash[ hash ] = uvs.length / 2;
            uvs.push( uv.x, uv.y );

            return uvsHash[ hash ];

        }

        data.data = {};

        data.data.vertices = vertices;
        data.data.normals = normals;
        if ( colors.length > 0 ) data.data.colors = colors;
        if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility
        data.data.faces = faces;

        return data;

    },

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( source ) {

        this.vertices = [];
        this.faces = [];
        this.faceVertexUvs = [ [] ];

        var vertices = source.vertices;

        for ( var i = 0, il = vertices.length; i < il; i ++ ) {

            this.vertices.push( vertices[ i ].clone() );

        }

        var faces = source.faces;

        for ( var i = 0, il = faces.length; i < il; i ++ ) {

            this.faces.push( faces[ i ].clone() );

        }

        for ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) {

            var faceVertexUvs = source.faceVertexUvs[ i ];

            if ( this.faceVertexUvs[ i ] === undefined ) {

                this.faceVertexUvs[ i ] = [];

            }

            for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) {

                var uvs = faceVertexUvs[ j ], uvsCopy = [];

                for ( var k = 0, kl = uvs.length; k < kl; k ++ ) {

                    var uv = uvs[ k ];

                    uvsCopy.push( uv.clone() );

                }

                this.faceVertexUvs[ i ].push( uvsCopy );

            }

        }

        return this;

    },

    dispose: function () {

        this.dispatchEvent( { type: 'dispose' } );

    }

};

THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype );

THREE.GeometryIdCount = 0;

// File:src/core/DirectGeometry.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.DirectGeometry = function () {

    Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );

    this.uuid = THREE.Math.generateUUID();

    this.name = '';
    this.type = 'DirectGeometry';

    this.indices = [];
    this.vertices = [];
    this.normals = [];
    this.colors = [];
    this.uvs = [];
    this.uvs2 = [];

    this.groups = [];

    this.morphTargets = {};

    this.skinWeights = [];
    this.skinIndices = [];

    // this.lineDistances = [];

    this.boundingBox = null;
    this.boundingSphere = null;

    // update flags

    this.verticesNeedUpdate = false;
    this.normalsNeedUpdate = false;
    this.colorsNeedUpdate = false;
    this.uvsNeedUpdate = false;
    this.groupsNeedUpdate = false;

};

THREE.DirectGeometry.prototype = {

    constructor: THREE.DirectGeometry,

    computeBoundingBox: THREE.Geometry.prototype.computeBoundingBox,
    computeBoundingSphere: THREE.Geometry.prototype.computeBoundingSphere,

    computeFaceNormals: function () {

        console.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' );

    },

    computeVertexNormals: function () {

        console.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' );

    },

    computeGroups: function ( geometry ) {

        var group;
        var groups = [];
        var materialIndex;

        var faces = geometry.faces;

        for ( var i = 0; i < faces.length; i ++ ) {

            var face = faces[ i ];

            // materials

            if ( face.materialIndex !== materialIndex ) {

                materialIndex = face.materialIndex;

                if ( group !== undefined ) {

                    group.count = ( i * 3 ) - group.start;
                    groups.push( group );

                }

                group = {
                    start: i * 3,
                    materialIndex: materialIndex
                };

            }

        }

        if ( group !== undefined ) {

            group.count = ( i * 3 ) - group.start;
            groups.push( group );

        }

        this.groups = groups;

    },

    fromGeometry: function ( geometry ) {

        var faces = geometry.faces;
        var vertices = geometry.vertices;
        var faceVertexUvs = geometry.faceVertexUvs;

        var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0;
        var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0;

        // morphs

        var morphTargets = geometry.morphTargets;
        var morphTargetsLength = morphTargets.length;

        if ( morphTargetsLength > 0 ) {

            var morphTargetsPosition = [];

            for ( var i = 0; i < morphTargetsLength; i ++ ) {

                morphTargetsPosition[ i ] = [];

            }

            this.morphTargets.position = morphTargetsPosition;

        }

        var morphNormals = geometry.morphNormals;
        var morphNormalsLength = morphNormals.length;

        if ( morphNormalsLength > 0 ) {

            var morphTargetsNormal = [];

            for ( var i = 0; i < morphNormalsLength; i ++ ) {

                morphTargetsNormal[ i ] = [];

            }

            this.morphTargets.normal = morphTargetsNormal;

        }

        // skins

        var skinIndices = geometry.skinIndices;
        var skinWeights = geometry.skinWeights;

        var hasSkinIndices = skinIndices.length === vertices.length;
        var hasSkinWeights = skinWeights.length === vertices.length;

        //

        for ( var i = 0; i < faces.length; i ++ ) {

            var face = faces[ i ];

            this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );

            var vertexNormals = face.vertexNormals;

            if ( vertexNormals.length === 3 ) {

                this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );

            } else {

                var normal = face.normal;

                this.normals.push( normal, normal, normal );

            }

            var vertexColors = face.vertexColors;

            if ( vertexColors.length === 3 ) {

                this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );

            } else {

                var color = face.color;

                this.colors.push( color, color, color );

            }

            if ( hasFaceVertexUv === true ) {

                var vertexUvs = faceVertexUvs[ 0 ][ i ];

                if ( vertexUvs !== undefined ) {

                    this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );

                } else {

                    console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i );

                    this.uvs.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() );

                }

            }

            if ( hasFaceVertexUv2 === true ) {

                var vertexUvs = faceVertexUvs[ 1 ][ i ];

                if ( vertexUvs !== undefined ) {

                    this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );

                } else {

                    console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i );

                    this.uvs2.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() );

                }

            }

            // morphs

            for ( var j = 0; j < morphTargetsLength; j ++ ) {

                var morphTarget = morphTargets[ j ].vertices;

                morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] );

            }

            for ( var j = 0; j < morphNormalsLength; j ++ ) {

                var morphNormal = morphNormals[ j ].vertexNormals[ i ];

                morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c );

            }

            // skins

            if ( hasSkinIndices ) {

                this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] );

            }

            if ( hasSkinWeights ) {

                this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] );

            }

        }

        this.computeGroups( geometry );

        this.verticesNeedUpdate = geometry.verticesNeedUpdate;
        this.normalsNeedUpdate = geometry.normalsNeedUpdate;
        this.colorsNeedUpdate = geometry.colorsNeedUpdate;
        this.uvsNeedUpdate = geometry.uvsNeedUpdate;
        this.groupsNeedUpdate = geometry.groupsNeedUpdate;

        return this;

    },

    dispose: function () {

        this.dispatchEvent( { type: 'dispose' } );

    }

};

THREE.EventDispatcher.prototype.apply( THREE.DirectGeometry.prototype );

// File:src/core/BufferGeometry.js

/**
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 */

THREE.BufferGeometry = function () {

    Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } );

    this.uuid = THREE.Math.generateUUID();

    this.name = '';
    this.type = 'BufferGeometry';

    this.index = null;
    this.attributes = {};

    this.morphAttributes = {};

    this.groups = [];

    this.boundingBox = null;
    this.boundingSphere = null;

    this.drawRange = { start: 0, count: Infinity };

};

THREE.BufferGeometry.prototype = {

    constructor: THREE.BufferGeometry,

    addIndex: function ( index ) {

        console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' );
        this.setIndex( index );

    },

    getIndex: function () {

        return this.index;

    },

    setIndex: function ( index ) {

        this.index = index;

    },

    addAttribute: function ( name, attribute ) {

        if ( attribute instanceof THREE.BufferAttribute === false && attribute instanceof THREE.InterleavedBufferAttribute === false ) {

            console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' );

            this.addAttribute( name, new THREE.BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) );

            return;

        }

        if ( name === 'index' ) {

            console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' );
            this.setIndex( attribute );

            return;

        }

        this.attributes[ name ] = attribute;

    },

    getAttribute: function ( name ) {

        return this.attributes[ name ];

    },

    removeAttribute: function ( name ) {

        delete this.attributes[ name ];

    },

    get drawcalls() {

        console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' );
        return this.groups;

    },

    get offsets() {

        console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' );
        return this.groups;

    },

    addDrawCall: function ( start, count, indexOffset ) {

        if ( indexOffset !== undefined ) {

            console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' );

        }

        console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' );
        this.addGroup( start, count );

    },

    clearDrawCalls: function () {

        console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' );
        this.clearGroups();

    },

    addGroup: function ( start, count, materialIndex ) {

        this.groups.push( {

            start: start,
            count: count,
            materialIndex: materialIndex !== undefined ? materialIndex : 0

        } );

    },

    clearGroups: function () {

        this.groups = [];

    },

    setDrawRange: function ( start, count ) {

        this.drawRange.start = start;
        this.drawRange.count = count;

    },

    applyMatrix: function ( matrix ) {

        var position = this.attributes.position;

        if ( position !== undefined ) {

            matrix.applyToVector3Array( position.array );
            position.needsUpdate = true;

        }

        var normal = this.attributes.normal;

        if ( normal !== undefined ) {

            var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );

            normalMatrix.applyToVector3Array( normal.array );
            normal.needsUpdate = true;

        }

        if ( this.boundingBox !== null ) {

            this.computeBoundingBox();

        }

        if ( this.boundingSphere !== null ) {

            this.computeBoundingSphere();

        }

    },

    rotateX: function () {

        // rotate geometry around world x-axis

        var m1;

        return function rotateX( angle ) {

            if ( m1 === undefined ) m1 = new THREE.Matrix4();

            m1.makeRotationX( angle );

            this.applyMatrix( m1 );

            return this;

        };

    }(),

    rotateY: function () {

        // rotate geometry around world y-axis

        var m1;

        return function rotateY( angle ) {

            if ( m1 === undefined ) m1 = new THREE.Matrix4();

            m1.makeRotationY( angle );

            this.applyMatrix( m1 );

            return this;

        };

    }(),

    rotateZ: function () {

        // rotate geometry around world z-axis

        var m1;

        return function rotateZ( angle ) {

            if ( m1 === undefined ) m1 = new THREE.Matrix4();

            m1.makeRotationZ( angle );

            this.applyMatrix( m1 );

            return this;

        };

    }(),

    translate: function () {

        // translate geometry

        var m1;

        return function translate( x, y, z ) {

            if ( m1 === undefined ) m1 = new THREE.Matrix4();

            m1.makeTranslation( x, y, z );

            this.applyMatrix( m1 );

            return this;

        };

    }(),

    scale: function () {

        // scale geometry

        var m1;

        return function scale( x, y, z ) {

            if ( m1 === undefined ) m1 = new THREE.Matrix4();

            m1.makeScale( x, y, z );

            this.applyMatrix( m1 );

            return this;

        };

    }(),

    lookAt: function () {

        var obj;

        return function lookAt( vector ) {

            if ( obj === undefined ) obj = new THREE.Object3D();

            obj.lookAt( vector );

            obj.updateMatrix();

            this.applyMatrix( obj.matrix );

        };

    }(),

    center: function () {

        this.computeBoundingBox();

        var offset = this.boundingBox.center().negate();

        this.translate( offset.x, offset.y, offset.z );

        return offset;

    },

    setFromObject: function ( object ) {

        // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this );

        var geometry = object.geometry;

        if ( object instanceof THREE.Points || object instanceof THREE.Line ) {

            var positions = new THREE.Float32Attribute( geometry.vertices.length * 3, 3 );
            var colors = new THREE.Float32Attribute( geometry.colors.length * 3, 3 );

            this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) );
            this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) );

            if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) {

                var lineDistances = new THREE.Float32Attribute( geometry.lineDistances.length, 1 );

                this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) );

            }

            if ( geometry.boundingSphere !== null ) {

                this.boundingSphere = geometry.boundingSphere.clone();

            }

            if ( geometry.boundingBox !== null ) {

                this.boundingBox = geometry.boundingBox.clone();

            }

        } else if ( object instanceof THREE.Mesh ) {

            if ( geometry instanceof THREE.Geometry ) {

                this.fromGeometry( geometry );

            }

        }

        return this;

    },

    updateFromObject: function ( object ) {

        var geometry = object.geometry;

        if ( object instanceof THREE.Mesh ) {

            var direct = geometry.__directGeometry;

            if ( direct === undefined ) {

                return this.fromGeometry( geometry );

            }

            direct.verticesNeedUpdate = geometry.verticesNeedUpdate;
            direct.normalsNeedUpdate = geometry.normalsNeedUpdate;
            direct.colorsNeedUpdate = geometry.colorsNeedUpdate;
            direct.uvsNeedUpdate = geometry.uvsNeedUpdate;
            direct.groupsNeedUpdate = geometry.groupsNeedUpdate;

            geometry.verticesNeedUpdate = false;
            geometry.normalsNeedUpdate = false;
            geometry.colorsNeedUpdate = false;
            geometry.uvsNeedUpdate = false;
            geometry.groupsNeedUpdate = false;

            geometry = direct;

        }

        if ( geometry.verticesNeedUpdate === true ) {

            var attribute = this.attributes.position;

            if ( attribute !== undefined ) {

                attribute.copyVector3sArray( geometry.vertices );
                attribute.needsUpdate = true;

            }

            geometry.verticesNeedUpdate = false;

        }

        if ( geometry.normalsNeedUpdate === true ) {

            var attribute = this.attributes.normal;

            if ( attribute !== undefined ) {

                attribute.copyVector3sArray( geometry.normals );
                attribute.needsUpdate = true;

            }

            geometry.normalsNeedUpdate = false;

        }

        if ( geometry.colorsNeedUpdate === true ) {

            var attribute = this.attributes.color;

            if ( attribute !== undefined ) {

                attribute.copyColorsArray( geometry.colors );
                attribute.needsUpdate = true;

            }

            geometry.colorsNeedUpdate = false;

        }

        if ( geometry.uvsNeedUpdate ) {

            var attribute = this.attributes.uv;

            if ( attribute !== undefined ) {

                attribute.copyVector2sArray( geometry.uvs );
                attribute.needsUpdate = true;

            }

            geometry.uvsNeedUpdate = false;

        }

        if ( geometry.lineDistancesNeedUpdate ) {

            var attribute = this.attributes.lineDistance;

            if ( attribute !== undefined ) {

                attribute.copyArray( geometry.lineDistances );
                attribute.needsUpdate = true;

            }

            geometry.lineDistancesNeedUpdate = false;

        }

        if ( geometry.groupsNeedUpdate ) {

            geometry.computeGroups( object.geometry );
            this.groups = geometry.groups;

            geometry.groupsNeedUpdate = false;

        }

        return this;

    },

    fromGeometry: function ( geometry ) {

        geometry.__directGeometry = new THREE.DirectGeometry().fromGeometry( geometry );

        return this.fromDirectGeometry( geometry.__directGeometry );

    },

    fromDirectGeometry: function ( geometry ) {

        var positions = new Float32Array( geometry.vertices.length * 3 );
        this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) );

        if ( geometry.normals.length > 0 ) {

            var normals = new Float32Array( geometry.normals.length * 3 );
            this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) );

        }

        if ( geometry.colors.length > 0 ) {

            var colors = new Float32Array( geometry.colors.length * 3 );
            this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) );

        }

        if ( geometry.uvs.length > 0 ) {

            var uvs = new Float32Array( geometry.uvs.length * 2 );
            this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) );

        }

        if ( geometry.uvs2.length > 0 ) {

            var uvs2 = new Float32Array( geometry.uvs2.length * 2 );
            this.addAttribute( 'uv2', new THREE.BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) );

        }

        if ( geometry.indices.length > 0 ) {

            var TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array;
            var indices = new TypeArray( geometry.indices.length * 3 );
            this.setIndex( new THREE.BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) );

        }

        // groups

        this.groups = geometry.groups;

        // morphs

        for ( var name in geometry.morphTargets ) {

            var array = [];
            var morphTargets = geometry.morphTargets[ name ];

            for ( var i = 0, l = morphTargets.length; i < l; i ++ ) {

                var morphTarget = morphTargets[ i ];

                var attribute = new THREE.Float32Attribute( morphTarget.length * 3, 3 );

                array.push( attribute.copyVector3sArray( morphTarget ) );

            }

            this.morphAttributes[ name ] = array;

        }

        // skinning

        if ( geometry.skinIndices.length > 0 ) {

            var skinIndices = new THREE.Float32Attribute( geometry.skinIndices.length * 4, 4 );
            this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) );

        }

        if ( geometry.skinWeights.length > 0 ) {

            var skinWeights = new THREE.Float32Attribute( geometry.skinWeights.length * 4, 4 );
            this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) );

        }

        //

        if ( geometry.boundingSphere !== null ) {

            this.boundingSphere = geometry.boundingSphere.clone();

        }

        if ( geometry.boundingBox !== null ) {

            this.boundingBox = geometry.boundingBox.clone();

        }

        return this;

    },

    computeBoundingBox: function () {

        var vector = new THREE.Vector3();

        return function () {

            if ( this.boundingBox === null ) {

                this.boundingBox = new THREE.Box3();

            }

            var positions = this.attributes.position.array;

            if ( positions ) {

                var bb = this.boundingBox;
                bb.makeEmpty();

                for ( var i = 0, il = positions.length; i < il; i += 3 ) {

                    vector.fromArray( positions, i );
                    bb.expandByPoint( vector );

                }

            }

            if ( positions === undefined || positions.length === 0 ) {

                this.boundingBox.min.set( 0, 0, 0 );
                this.boundingBox.max.set( 0, 0, 0 );

            }

            if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {

                console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this );

            }

        };

    }(),

    computeBoundingSphere: function () {

        var box = new THREE.Box3();
        var vector = new THREE.Vector3();

        return function () {

            if ( this.boundingSphere === null ) {

                this.boundingSphere = new THREE.Sphere();

            }

            var positions = this.attributes.position.array;

            if ( positions ) {

                box.makeEmpty();

                var center = this.boundingSphere.center;

                for ( var i = 0, il = positions.length; i < il; i += 3 ) {

                    vector.fromArray( positions, i );
                    box.expandByPoint( vector );

                }

                box.center( center );

                // hoping to find a boundingSphere with a radius smaller than the
                // boundingSphere of the boundingBox: sqrt(3) smaller in the best case

                var maxRadiusSq = 0;

                for ( var i = 0, il = positions.length; i < il; i += 3 ) {

                    vector.fromArray( positions, i );
                    maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );

                }

                this.boundingSphere.radius = Math.sqrt( maxRadiusSq );

                if ( isNaN( this.boundingSphere.radius ) ) {

                    console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this );

                }

            }

        };

    }(),

    computeFaceNormals: function () {

        // backwards compatibility

    },

    computeVertexNormals: function () {

        var index = this.index;
        var attributes = this.attributes;
        var groups = this.groups;

        if ( attributes.position ) {

            var positions = attributes.position.array;

            if ( attributes.normal === undefined ) {

                this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) );

            } else {

                // reset existing normals to zero

                var normals = attributes.normal.array;

                for ( var i = 0, il = normals.length; i < il; i ++ ) {

                    normals[ i ] = 0;

                }

            }

            var normals = attributes.normal.array;

            var vA, vB, vC,

                pA = new THREE.Vector3(),
                pB = new THREE.Vector3(),
                pC = new THREE.Vector3(),

                cb = new THREE.Vector3(),
                ab = new THREE.Vector3();

            // indexed elements

            if ( index ) {

                var indices = index.array;

                if ( groups.length === 0 ) {

                    this.addGroup( 0, indices.length );

                }

                for ( var j = 0, jl = groups.length; j < jl; ++ j ) {

                    var group = groups[ j ];

                    var start = group.start;
                    var count = group.count;

                    for ( var i = start, il = start + count; i < il; i += 3 ) {

                        vA = indices[ i + 0 ] * 3;
                        vB = indices[ i + 1 ] * 3;
                        vC = indices[ i + 2 ] * 3;

                        pA.fromArray( positions, vA );
                        pB.fromArray( positions, vB );
                        pC.fromArray( positions, vC );

                        cb.subVectors( pC, pB );
                        ab.subVectors( pA, pB );
                        cb.cross( ab );

                        normals[ vA ] += cb.x;
                        normals[ vA + 1 ] += cb.y;
                        normals[ vA + 2 ] += cb.z;

                        normals[ vB ] += cb.x;
                        normals[ vB + 1 ] += cb.y;
                        normals[ vB + 2 ] += cb.z;

                        normals[ vC ] += cb.x;
                        normals[ vC + 1 ] += cb.y;
                        normals[ vC + 2 ] += cb.z;

                    }

                }

            } else {

                // non-indexed elements (unconnected triangle soup)

                for ( var i = 0, il = positions.length; i < il; i += 9 ) {

                    pA.fromArray( positions, i );
                    pB.fromArray( positions, i + 3 );
                    pC.fromArray( positions, i + 6 );

                    cb.subVectors( pC, pB );
                    ab.subVectors( pA, pB );
                    cb.cross( ab );

                    normals[ i ] = cb.x;
                    normals[ i + 1 ] = cb.y;
                    normals[ i + 2 ] = cb.z;

                    normals[ i + 3 ] = cb.x;
                    normals[ i + 4 ] = cb.y;
                    normals[ i + 5 ] = cb.z;

                    normals[ i + 6 ] = cb.x;
                    normals[ i + 7 ] = cb.y;
                    normals[ i + 8 ] = cb.z;

                }

            }

            this.normalizeNormals();

            attributes.normal.needsUpdate = true;

        }

    },

    computeTangents: function () {

        console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' );

    },

    computeOffsets: function ( size ) {

        console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.')

    },

    merge: function ( geometry, offset ) {

        if ( geometry instanceof THREE.BufferGeometry === false ) {

            console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry );
            return;

        }

        if ( offset === undefined ) offset = 0;

        var attributes = this.attributes;

        for ( var key in attributes ) {

            if ( geometry.attributes[ key ] === undefined ) continue;

            var attribute1 = attributes[ key ];
            var attributeArray1 = attribute1.array;

            var attribute2 = geometry.attributes[ key ];
            var attributeArray2 = attribute2.array;

            var attributeSize = attribute2.itemSize;

            for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) {

                attributeArray1[ j ] = attributeArray2[ i ];

            }

        }

        return this;

    },

    normalizeNormals: function () {

        var normals = this.attributes.normal.array;

        var x, y, z, n;

        for ( var i = 0, il = normals.length; i < il; i += 3 ) {

            x = normals[ i ];
            y = normals[ i + 1 ];
            z = normals[ i + 2 ];

            n = 1.0 / Math.sqrt( x * x + y * y + z * z );

            normals[ i ] *= n;
            normals[ i + 1 ] *= n;
            normals[ i + 2 ] *= n;

        }

    },

    toJSON: function () {

        var data = {
            metadata: {
                version: 4.4,
                type: 'BufferGeometry',
                generator: 'BufferGeometry.toJSON'
            }
        };

        // standard BufferGeometry serialization

        data.uuid = this.uuid;
        data.type = this.type;
        if ( this.name !== '' ) data.name = this.name;

        if ( this.parameters !== undefined ) {

            var parameters = this.parameters;

            for ( var key in parameters ) {

                if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];

            }

            return data;

        }

        data.data = { attributes: {} };

        var index = this.index;

        if ( index !== null ) {

            var array = Array.prototype.slice.call( index.array );

            data.data.index = {
                type: index.array.constructor.name,
                array: array
            };

        }

        var attributes = this.attributes;

        for ( var key in attributes ) {

            var attribute = attributes[ key ];

            var array = Array.prototype.slice.call( attribute.array );

            data.data.attributes[ key ] = {
                itemSize: attribute.itemSize,
                type: attribute.array.constructor.name,
                array: array
            };

        }

        var groups = this.groups;

        if ( groups.length > 0 ) {

            data.data.groups = JSON.parse( JSON.stringify( groups ) );

        }

        var boundingSphere = this.boundingSphere;

        if ( boundingSphere !== null ) {

            data.data.boundingSphere = {
                center: boundingSphere.center.toArray(),
                radius: boundingSphere.radius
            };

        }

        return data;

    },

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( source ) {

        var index = source.index;

        if ( index !== null ) {

            this.setIndex( index.clone() );

        }

        var attributes = source.attributes;

        for ( var name in attributes ) {

            var attribute = attributes[ name ];
            this.addAttribute( name, attribute.clone() );

        }

        var groups = source.groups;

        for ( var i = 0, l = groups.length; i < l; i ++ ) {

            var group = groups[ i ];
            this.addGroup( group.start, group.count );

        }

        return this;

    },

    dispose: function () {

        this.dispatchEvent( { type: 'dispose' } );

    }

};

THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype );

THREE.BufferGeometry.MaxIndex = 65535;

// File:src/core/InstancedBufferGeometry.js

/**
 * @author benaadams / https://twitter.com/ben_a_adams
 */

THREE.InstancedBufferGeometry = function () {

    THREE.BufferGeometry.call( this );

    this.type = 'InstancedBufferGeometry';
    this.maxInstancedCount = undefined;

};

THREE.InstancedBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
THREE.InstancedBufferGeometry.prototype.constructor = THREE.InstancedBufferGeometry;

THREE.InstancedBufferGeometry.prototype.addGroup = function ( start, count, instances ) {

    this.groups.push( {

        start: start,
        count: count,
        instances: instances

    } );

};

THREE.InstancedBufferGeometry.prototype.copy = function ( source ) {

    var index = source.index;

    if ( index !== null ) {

        this.setIndex( index.clone() );

    }

    var attributes = source.attributes;

    for ( var name in attributes ) {

        var attribute = attributes[ name ];
        this.addAttribute( name, attribute.clone() );

    }

    var groups = source.groups;

    for ( var i = 0, l = groups.length; i < l; i ++ ) {

        var group = groups[ i ];
        this.addGroup( group.start, group.count, group.instances );

    }

    return this;

};

THREE.EventDispatcher.prototype.apply( THREE.InstancedBufferGeometry.prototype );

// File:src/animation/AnimationAction.js

/**
 *
 * A clip that has been explicitly scheduled.
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 */

THREE.AnimationAction = function ( clip, startTime, timeScale, weight, loop ) {

    if ( clip === undefined ) throw new Error( 'clip is null' );
    this.clip = clip;
    this.localRoot = null;
    this.startTime = startTime || 0;
    this.timeScale = timeScale || 1;
    this.weight = weight || 1;
    this.loop = loop || THREE.LoopRepeat;
    this.loopCount = 0;
    this.enabled = true;	// allow for easy disabling of the action.

    this.actionTime = - this.startTime;
    this.clipTime = 0;

    this.propertyBindings = [];
};

/*
THREE.LoopOnce = 2200;
THREE.LoopRepeat = 2201;
THREE.LoopPingPing = 2202;
*/

THREE.AnimationAction.prototype = {

    constructor: THREE.AnimationAction,

    setLocalRoot: function( localRoot ) {

        this.localRoot = localRoot;

        return this;

    },

    updateTime: function( clipDeltaTime ) {

        var previousClipTime = this.clipTime;
        var previousLoopCount = this.loopCount;
        var previousActionTime = this.actionTime;

        var duration = this.clip.duration;

        this.actionTime = this.actionTime + clipDeltaTime;

        if ( this.loop === THREE.LoopOnce ) {

            this.loopCount = 0;
            this.clipTime = Math.min( Math.max( this.actionTime, 0 ), duration );

            // if time is changed since last time, see if we have hit a start/end limit
            if ( this.clipTime !== previousClipTime ) {

                if ( this.clipTime === duration ) {

                    this.mixer.dispatchEvent( { type: 'finished', action: this, direction: 1 } );

                } else if ( this.clipTime === 0 ) {

                    this.mixer.dispatchEvent( { type: 'finished', action: this, direction: -1 } );

                }

            }


            return this.clipTime;

        }

        this.loopCount = Math.floor( this.actionTime / duration );

        var newClipTime = this.actionTime - this.loopCount * duration;
        newClipTime = newClipTime % duration;

        // if we are ping pong looping, ensure that we go backwards when appropriate
        if ( this.loop == THREE.LoopPingPong ) {

            if ( Math.abs( this.loopCount % 2 ) === 1 ) {

                newClipTime = duration - newClipTime;

            }

        }

        this.clipTime = newClipTime;

        if ( this.loopCount !== previousLoopCount ) {

            this.mixer.dispatchEvent( { type: 'loop', action: this, loopDelta: ( this.loopCount - this.loopCount ) } );

        }

        return this.clipTime;

    },

    syncWith: function( action ) {

        this.actionTime = action.actionTime;
        this.timeScale = action.timeScale;

        return this;
    },

    warpToDuration: function( duration ) {

        this.timeScale = this.clip.duration / duration;

        return this;
    },

    init: function( time ) {

        this.clipTime = time - this.startTime;

        return this;

    },

    update: function( clipDeltaTime ) {

        this.updateTime( clipDeltaTime );

        var clipResults = this.clip.getAt( this.clipTime );

        return clipResults;

    },

    getTimeScaleAt: function( time ) {

        if ( this.timeScale.getAt ) {
            // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip
            return this.timeScale.getAt( time );

        }

        return this.timeScale;

    },

    getWeightAt: function( time ) {

        if ( this.weight.getAt ) {
            // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip
            return this.weight.getAt( time );

        }

        return this.weight;

    }

};

// File:src/animation/AnimationClip.js

/**
 *
 * Reusable set of Tracks that represent an animation.
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 */

THREE.AnimationClip = function ( name, duration, tracks ) {

    this.name = name;
    this.tracks = tracks;
    this.duration = ( duration !== undefined ) ? duration : -1;

    // this means it should figure out its duration by scanning the tracks
    if ( this.duration < 0 ) {
        for ( var i = 0; i < this.tracks.length; i ++ ) {
            var track = this.tracks[i];
            this.duration = Math.max( track.keys[ track.keys.length - 1 ].time );
        }
    }

    // maybe only do these on demand, as doing them here could potentially slow down loading
    // but leaving these here during development as this ensures a lot of testing of these functions
    this.trim();
    this.optimize();

    this.results = [];

};

THREE.AnimationClip.prototype = {

    constructor: THREE.AnimationClip,

    getAt: function( clipTime ) {

        clipTime = Math.max( 0, Math.min( clipTime, this.duration ) );

        for ( var i = 0; i < this.tracks.length; i ++ ) {

            var track = this.tracks[ i ];

            this.results[ i ] = track.getAt( clipTime );

        }

        return this.results;
    },

    trim: function() {

        for ( var i = 0; i < this.tracks.length; i ++ ) {

            this.tracks[ i ].trim( 0, this.duration );

        }

        return this;

    },

    optimize: function() {

        for ( var i = 0; i < this.tracks.length; i ++ ) {

            this.tracks[ i ].optimize();

        }

        return this;

    }

};


THREE.AnimationClip.CreateFromMorphTargetSequence = function( name, morphTargetSequence, fps ) {


    var numMorphTargets = morphTargetSequence.length;
    var tracks = [];

    for ( var i = 0; i < numMorphTargets; i ++ ) {

        var keys = [];

        keys.push( { time: ( i + numMorphTargets - 1 ) % numMorphTargets, value: 0 } );
        keys.push( { time: i, value: 1 } );
        keys.push( { time: ( i + 1 ) % numMorphTargets, value: 0 } );

        keys.sort( THREE.KeyframeTrack.keyComparer );

        // if there is a key at the first frame, duplicate it as the last frame as well for perfect loop.
        if ( keys[0].time === 0 ) {
            keys.push( {
                time: numMorphTargets,
                value: keys[0].value
            });
        }

        tracks.push( new THREE.NumberKeyframeTrack( '.morphTargetInfluences[' + morphTargetSequence[i].name + ']', keys ).scale( 1.0 / fps ) );
    }

    return new THREE.AnimationClip( name, -1, tracks );

};

THREE.AnimationClip.findByName = function( clipArray, name ) {

    for ( var i = 0; i < clipArray.length; i ++ ) {

        if ( clipArray[i].name === name ) {

            return clipArray[i];

        }
    }

    return null;

};

THREE.AnimationClip.CreateClipsFromMorphTargetSequences = function( morphTargets, fps ) {

    var animationToMorphTargets = {};

    // tested with https://regex101.com/ on trick sequences such flamingo_flyA_003, flamingo_run1_003, crdeath0059
    var pattern = /^([\w-]*?)([\d]+)$/;

    // sort morph target names into animation groups based patterns like Walk_001, Walk_002, Run_001, Run_002
    for ( var i = 0, il = morphTargets.length; i < il; i ++ ) {

        var morphTarget = morphTargets[ i ];
        var parts = morphTarget.name.match( pattern );

        if ( parts && parts.length > 1 ) {

            var name = parts[ 1 ];

            var animationMorphTargets = animationToMorphTargets[ name ];
            if ( ! animationMorphTargets ) {
                animationToMorphTargets[ name ] = animationMorphTargets = [];
            }

            animationMorphTargets.push( morphTarget );

        }

    }

    var clips = [];

    for ( var name in animationToMorphTargets ) {

        clips.push( THREE.AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps ) );
    }

    return clips;

};

// parse the standard JSON format for clips
THREE.AnimationClip.parse = function( json ) {

    var tracks = [];

    for ( var i = 0; i < json.tracks.length; i ++ ) {

        tracks.push( THREE.KeyframeTrack.parse( json.tracks[i] ).scale( 1.0 / json.fps ) );

    }

    return new THREE.AnimationClip( json.name, json.duration, tracks );

};


// parse the animation.hierarchy format
THREE.AnimationClip.parseAnimation = function( animation, bones, nodeName ) {

    if ( ! animation ) {
        console.error( "  no animation in JSONLoader data" );
        return null;
    }

    var convertTrack = function( trackName, animationKeys, propertyName, trackType, animationKeyToValueFunc ) {

        var keys = [];

        for ( var k = 0; k < animationKeys.length; k ++ ) {

            var animationKey = animationKeys[k];

            if ( animationKey[propertyName] !== undefined ) {

                keys.push( { time: animationKey.time, value: animationKeyToValueFunc( animationKey ) } );
            }

        }

        // only return track if there are actually keys.
        if ( keys.length > 0 ) {

            return new trackType( trackName, keys );

        }

        return null;

    };

    var tracks = [];

    var clipName = animation.name || 'default';
    var duration = animation.length || -1; // automatic length determination in AnimationClip.
    var fps = animation.fps || 30;

    var hierarchyTracks = animation.hierarchy || [];

    for ( var h = 0; h < hierarchyTracks.length; h ++ ) {

        var animationKeys = hierarchyTracks[ h ].keys;

        // skip empty tracks
        if ( ! animationKeys || animationKeys.length == 0 ) {
            continue;
        }

        // process morph targets in a way exactly compatible with AnimationHandler.init( animation )
        if ( animationKeys[0].morphTargets ) {

            // figure out all morph targets used in this track
            var morphTargetNames = {};
            for ( var k = 0; k < animationKeys.length; k ++ ) {

                if ( animationKeys[k].morphTargets ) {
                    for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {

                        morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1;
                    }
                }

            }

            // create a track for each morph target with all zero morphTargetInfluences except for the keys in which the morphTarget is named.
            for ( var morphTargetName in morphTargetNames ) {

                var keys = [];

                for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {

                    var animationKey = animationKeys[k];

                    keys.push( {
                        time: animationKey.time,
                        value: (( animationKey.morphTarget === morphTargetName ) ? 1 : 0 )
                    });

                }

                tracks.push( new THREE.NumberKeyframeTrack( nodeName + '.morphTargetInfluence[' + morphTargetName + ']', keys ) );

            }

            duration = morphTargetNames.length * ( fps || 1.0 );

        } else {

            var boneName = nodeName + '.bones[' + bones[ h ].name + ']';

            // track contains positions...
            var positionTrack = convertTrack( boneName + '.position', animationKeys, 'pos', THREE.VectorKeyframeTrack, function( animationKey ) {
                return new THREE.Vector3().fromArray( animationKey.pos )
            } );

            if ( positionTrack ) tracks.push( positionTrack );

            // track contains quaternions...
            var quaternionTrack = convertTrack( boneName + '.quaternion', animationKeys, 'rot', THREE.QuaternionKeyframeTrack, function( animationKey ) {
                if ( animationKey.rot.slerp ) {
                    return animationKey.rot.clone();
                } else {
                    return new THREE.Quaternion().fromArray( animationKey.rot );
                }
            } );

            if ( quaternionTrack ) tracks.push( quaternionTrack );

            // track contains quaternions...
            var scaleTrack = convertTrack( boneName + '.scale', animationKeys, 'scl', THREE.VectorKeyframeTrack, function( animationKey ) {
                return new THREE.Vector3().fromArray( animationKey.scl )
            } );

            if ( scaleTrack ) tracks.push( scaleTrack );

        }
    }

    if ( tracks.length === 0 ) {

        return null;

    }

    var clip = new THREE.AnimationClip( clipName, duration, tracks );

    return clip;

};

// File:src/animation/AnimationMixer.js

/**
 *
 * Mixes together the AnimationClips scheduled by AnimationActions and applies them to the root and subtree
 *
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 */

THREE.AnimationMixer = function( root ) {

    this.root = root;
    this.time = 0;
    this.timeScale = 1.0;
    this.actions = [];
    this.propertyBindingMap = {};

};

THREE.AnimationMixer.prototype = {

    constructor: THREE.AnimationMixer,

    addAction: function( action ) {

        // TODO: check for duplicate action names?  Or provide each action with a UUID?

        this.actions.push( action );
        action.init( this.time );
        action.mixer = this;

        var tracks = action.clip.tracks;

        var root = action.localRoot || this.root;

        for ( var i = 0; i < tracks.length; i ++ ) {

            var track = tracks[ i ];

            var propertyBindingKey = root.uuid + '-' + track.name;
            var propertyBinding = this.propertyBindingMap[ propertyBindingKey ];

            if ( propertyBinding === undefined ) {

                propertyBinding = new THREE.PropertyBinding( root, track.name );
                this.propertyBindingMap[ propertyBindingKey ] = propertyBinding;

            }

            // push in the same order as the tracks.
            action.propertyBindings.push( propertyBinding );

            // track usages of shared property bindings, because if we leave too many around, the mixer can get slow
            propertyBinding.referenceCount += 1;

        }

    },

    removeAllActions: function() {

        for ( var i = 0; i < this.actions.length; i ++ ) {

            this.actions[i].mixer = null;

        }

        // unbind all property bindings
        for ( var properyBindingKey in this.propertyBindingMap ) {

            this.propertyBindingMap[ properyBindingKey ].unbind();

        }

        this.actions = [];
        this.propertyBindingMap = {};

        return this;

    },

    removeAction: function( action ) {

        var index = this.actions.indexOf( action );

        if ( index !== - 1 ) {

            this.actions.splice( index, 1 );
            action.mixer = null;

        }


        // remove unused property bindings because if we leave them around the mixer can get slow
        var root = action.localRoot || this.root;
        var tracks = action.clip.tracks;

        for ( var i = 0; i < tracks.length; i ++ ) {

            var track = tracks[ i ];

            var propertyBindingKey = root.uuid + '-' + track.name;
            var propertyBinding = this.propertyBindingMap[ propertyBindingKey ];

            propertyBinding.referenceCount -= 1;

            if ( propertyBinding.referenceCount <= 0 ) {

                propertyBinding.unbind();

                delete this.propertyBindingMap[ propertyBindingKey ];

            }
        }

        return this;

    },

    // can be optimized if needed
    findActionByName: function( name ) {

        for ( var i = 0; i < this.actions.length; i ++ ) {

            if ( this.actions[i].name === name ) return this.actions[i];

        }

        return null;

    },

    play: function( action, optionalFadeInDuration ) {

        action.startTime = this.time;
        this.addAction( action );

        return this;

    },

    fadeOut: function( action, duration ) {

        var keys = [];

        keys.push( { time: this.time, value: 1 } );
        keys.push( { time: this.time + duration, value: 0 } );

        action.weight = new THREE.NumberKeyframeTrack( "weight", keys );

        return this;

    },

    fadeIn: function( action, duration ) {

        var keys = [];

        keys.push( { time: this.time, value: 0 } );
        keys.push( { time: this.time + duration, value: 1 } );

        action.weight = new THREE.NumberKeyframeTrack( "weight", keys );

        return this;

    },

    warp: function( action, startTimeScale, endTimeScale, duration ) {

        var keys = [];

        keys.push( { time: this.time, value: startTimeScale } );
        keys.push( { time: this.time + duration, value: endTimeScale } );

        action.timeScale = new THREE.NumberKeyframeTrack( "timeScale", keys );

        return this;

    },

    crossFade: function( fadeOutAction, fadeInAction, duration, warp ) {

        this.fadeOut( fadeOutAction, duration );
        this.fadeIn( fadeInAction, duration );

        if ( warp ) {

            var startEndRatio = fadeOutAction.clip.duration / fadeInAction.clip.duration;
            var endStartRatio = 1.0 / startEndRatio;

            this.warp( fadeOutAction, 1.0, startEndRatio, duration );
            this.warp( fadeInAction, endStartRatio, 1.0, duration );

        }

        return this;

    },

    update: function( deltaTime ) {

        var mixerDeltaTime = deltaTime * this.timeScale;
        this.time += mixerDeltaTime;

        for ( var i = 0; i < this.actions.length; i ++ ) {

            var action = this.actions[i];

            var weight = action.getWeightAt( this.time );

            var actionTimeScale = action.getTimeScaleAt( this.time );
            var actionDeltaTime = mixerDeltaTime * actionTimeScale;

            var actionResults = action.update( actionDeltaTime );

            if ( action.weight <= 0 || ! action.enabled ) continue;

            for ( var j = 0; j < actionResults.length; j ++ ) {

                var name = action.clip.tracks[j].name;

                action.propertyBindings[ j ].accumulate( actionResults[j], weight );

            }

        }

        // apply to nodes
        for ( var propertyBindingKey in this.propertyBindingMap ) {

            this.propertyBindingMap[ propertyBindingKey ].apply();

        }

        return this;

    }

};

THREE.EventDispatcher.prototype.apply( THREE.AnimationMixer.prototype );

// File:src/animation/AnimationUtils.js

/**
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 */

THREE.AnimationUtils = {

    getEqualsFunc: function( exemplarValue ) {

        if ( exemplarValue.equals ) {
            return function equals_object( a, b ) {
                return a.equals( b );
            }
        }

        return function equals_primitive( a, b ) {
            return ( a === b );
        };

    },

    clone: function( exemplarValue ) {

        var typeName = typeof exemplarValue;
        if ( typeName === "object" ) {
            if ( exemplarValue.clone ) {
                return exemplarValue.clone();
            }
            console.error( "can not figure out how to copy exemplarValue", exemplarValue );
        }

        return exemplarValue;

    },

    lerp: function( a, b, alpha, interTrack ) {

        var lerpFunc = THREE.AnimationUtils.getLerpFunc( a, interTrack );

        return lerpFunc( a, b, alpha );

    },

    lerp_object: function( a, b, alpha ) {
        return a.lerp( b, alpha );
    },

    slerp_object: function( a, b, alpha ) {
        return a.slerp( b, alpha );
    },

    lerp_number: function( a, b, alpha ) {
        return a * ( 1 - alpha ) + b * alpha;
    },

    lerp_boolean: function( a, b, alpha ) {
        return ( alpha < 0.5 ) ? a : b;
    },

    lerp_boolean_immediate: function( a, b, alpha ) {
        return a;
    },

    lerp_string: function( a, b, alpha ) {
        return ( alpha < 0.5 ) ? a : b;
    },

    lerp_string_immediate: function( a, b, alpha ) {
        return a;
    },

    // NOTE: this is an accumulator function that modifies the first argument (e.g. a).	This is to minimize memory alocations.
    getLerpFunc: function( exemplarValue, interTrack ) {

        if ( exemplarValue === undefined || exemplarValue === null ) throw new Error( "examplarValue is null" );

        var typeName = typeof exemplarValue;

        switch( typeName ) {

            case "object":
                if ( exemplarValue.lerp ) {
                    return THREE.AnimationUtils.lerp_object;
                }

                if ( exemplarValue.slerp ) {
                    return THREE.AnimationUtils.slerp_object;
                }
                break;

            case "number":
                return THREE.AnimationUtils.lerp_number;

            case "boolean":
                if ( interTrack ) {
                    return THREE.AnimationUtils.lerp_boolean;
                } else {
                    return THREE.AnimationUtils.lerp_boolean_immediate;
                }

            case "string":
                if ( interTrack ) {
                    return THREE.AnimationUtils.lerp_string;
                } else {
                    return THREE.AnimationUtils.lerp_string_immediate;
                }

        }

    }

};

// File:src/animation/KeyframeTrack.js

/**
 *
 * A Track that returns a keyframe interpolated value, currently linearly interpolated
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 */

THREE.KeyframeTrack = function ( name, keys ) {

    if ( name === undefined ) throw new Error( "track name is undefined" );
    if ( keys === undefined || keys.length === 0 ) throw new Error( "no keys in track named " + name );

    this.name = name;
    this.keys = keys;	// time in seconds, value as value

    // the index of the last result, used as a starting point for local search.
    this.lastIndex = 0;

    this.validate();
    this.optimize();

};

THREE.KeyframeTrack.prototype = {

    constructor: THREE.KeyframeTrack,

    getAt: function( time ) {


        // this can not go higher than this.keys.length.
        while( ( this.lastIndex < this.keys.length ) && ( time >= this.keys[this.lastIndex].time ) ) {
            this.lastIndex ++;
        };

        // this can not go lower than 0.
        while( ( this.lastIndex > 0 ) && ( time < this.keys[this.lastIndex - 1].time ) ) {
            this.lastIndex --;
        }

        if ( this.lastIndex >= this.keys.length ) {

            this.setResult( this.keys[ this.keys.length - 1 ].value );

            return this.result;

        }

        if ( this.lastIndex === 0 ) {

            this.setResult( this.keys[ 0 ].value );

            return this.result;

        }

        var prevKey = this.keys[ this.lastIndex - 1 ];
        this.setResult( prevKey.value );

        // if true, means that prev/current keys are identical, thus no interpolation required.
        if ( prevKey.constantToNext ) {

            return this.result;

        }

        // linear interpolation to start with
        var currentKey = this.keys[ this.lastIndex ];
        var alpha = ( time - prevKey.time ) / ( currentKey.time - prevKey.time );
        this.result = this.lerpValues( this.result, currentKey.value, alpha );

        return this.result;

    },

    // move all keyframes either forwards or backwards in time
    shift: function( timeOffset ) {

        if ( timeOffset !== 0.0 ) {

            for ( var i = 0; i < this.keys.length; i ++ ) {
                this.keys[i].time += timeOffset;
            }

        }

        return this;

    },

    // scale all keyframe times by a factor (useful for frame <-> seconds conversions)
    scale: function( timeScale ) {

        if ( timeScale !== 1.0 ) {

            for ( var i = 0; i < this.keys.length; i ++ ) {
                this.keys[i].time *= timeScale;
            }

        }

        return this;

    },

    // removes keyframes before and after animation without changing any values within the range [startTime, endTime].
    // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
    trim: function( startTime, endTime ) {

        var firstKeysToRemove = 0;
        for ( var i = 1; i < this.keys.length; i ++ ) {
            if ( this.keys[i] <= startTime ) {
                firstKeysToRemove ++;
            }
        }

        var lastKeysToRemove = 0;
        for ( var i = this.keys.length - 2; i > 0; i ++ ) {
            if ( this.keys[i] >= endTime ) {
                lastKeysToRemove ++;
            } else {
                break;
            }
        }

        // remove last keys first because it doesn't affect the position of the first keys (the otherway around doesn't work as easily)
        if ( ( firstKeysToRemove + lastKeysToRemove ) > 0 ) {
            this.keys = this.keys.splice( firstKeysToRemove, this.keys.length - lastKeysToRemove - firstKeysToRemove );;
        }

        return this;

    },

    /* NOTE: This is commented out because we really shouldn't have to handle unsorted key lists
	         Tracks with out of order keys should be considered to be invalid.  - bhouston
	sort: function() {

		this.keys.sort( THREE.KeyframeTrack.keyComparer );

		return this;

	},*/

    // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
    // One could eventually ensure that all key.values in a track are all of the same type (otherwise interpolation makes no sense.)
    validate: function() {

        var prevKey = null;

        if ( this.keys.length === 0 ) {
            console.error( "  track is empty, no keys", this );
            return;
        }

        for ( var i = 0; i < this.keys.length; i ++ ) {

            var currKey = this.keys[i];

            if ( ! currKey ) {
                console.error( "  key is null in track", this, i );
                return;
            }

            if ( ( typeof currKey.time ) !== 'number' || isNaN( currKey.time ) ) {
                console.error( "  key.time is not a valid number", this, i, currKey );
                return;
            }

            if ( currKey.value === undefined || currKey.value === null) {
                console.error( "  key.value is null in track", this, i, currKey );
                return;
            }

            if ( prevKey && prevKey.time > currKey.time ) {
                console.error( "  key.time is less than previous key time, out of order keys", this, i, currKey, prevKey );
                return;
            }

            prevKey = currKey;

        }

        return this;

    },

    // currently only removes equivalent sequential keys (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0), which are common in morph target animations
    optimize: function() {

        var newKeys = [];
        var prevKey = this.keys[0];
        newKeys.push( prevKey );

        var equalsFunc = THREE.AnimationUtils.getEqualsFunc( prevKey.value );

        for ( var i = 1; i < this.keys.length - 1; i ++ ) {
            var currKey = this.keys[i];
            var nextKey = this.keys[i+1];

            // if prevKey & currKey are the same time, remove currKey.  If you want immediate adjacent keys, use an epsilon offset
            // it is not possible to have two keys at the same time as we sort them.  The sort is not stable on keys with the same time.
            if ( ( prevKey.time === currKey.time ) ) {

                continue;

            }

            // remove completely unnecessary keyframes that are the same as their prev and next keys
            if ( this.compareValues( prevKey.value, currKey.value ) && this.compareValues( currKey.value, nextKey.value ) ) {

                continue;

            }

            // determine if interpolation is required
            prevKey.constantToNext = this.compareValues( prevKey.value, currKey.value );

            newKeys.push( currKey );
            prevKey = currKey;
        }
        newKeys.push( this.keys[ this.keys.length - 1 ] );

        this.keys = newKeys;

        return this;

    }

};

THREE.KeyframeTrack.keyComparer = function keyComparator(key0, key1) {
    return key0.time - key1.time;
};

THREE.KeyframeTrack.parse = function( json ) {

    if ( json.type === undefined ) throw new Error( "track type undefined, can not parse" );

    var trackType = THREE.KeyframeTrack.GetTrackTypeForTypeName( json.type );

    return trackType.parse( json );

};

THREE.KeyframeTrack.GetTrackTypeForTypeName = function( typeName ) {
    switch( typeName.toLowerCase() ) {
        case "vector":
        case "vector2":
        case "vector3":
        case "vector4":
            return THREE.VectorKeyframeTrack;

        case "quaternion":
            return THREE.QuaternionKeyframeTrack;

        case "integer":
        case "scalar":
        case "double":
        case "float":
        case "number":
            return THREE.NumberKeyframeTrack;

        case "bool":
        case "boolean":
            return THREE.BooleanKeyframeTrack;

        case "string":
            return THREE.StringKeyframeTrack;
    };

    throw new Error( "Unsupported typeName: " + typeName );
};

// File:src/animation/PropertyBinding.js

/**
 *
 * A track bound to a real value in the scene graph.
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 */

THREE.PropertyBinding = function ( rootNode, trackName ) {

    this.rootNode = rootNode;
    this.trackName = trackName;
    this.referenceCount = 0;
    this.originalValue = null; // the value of the property before it was controlled by this binding

    var parseResults = THREE.PropertyBinding.parseTrackName( trackName );

    this.directoryName = parseResults.directoryName;
    this.nodeName = parseResults.nodeName;
    this.objectName = parseResults.objectName;
    this.objectIndex = parseResults.objectIndex;
    this.propertyName = parseResults.propertyName;
    this.propertyIndex = parseResults.propertyIndex;

    this.node = THREE.PropertyBinding.findNode( rootNode, this.nodeName ) || rootNode;

    this.cumulativeValue = null;
    this.cumulativeWeight = 0;
};

THREE.PropertyBinding.prototype = {

    constructor: THREE.PropertyBinding,

    reset: function() {

        this.cumulativeValue = null;
        this.cumulativeWeight = 0;

    },

    accumulate: function( value, weight ) {

        if ( ! this.isBound ) this.bind();

        if ( this.cumulativeWeight === 0 ) {

            if ( weight > 0 ) {

                if ( this.cumulativeValue === null ) {
                    this.cumulativeValue = THREE.AnimationUtils.clone( value );
                }
                this.cumulativeWeight = weight;

            }

        } else {

            var lerpAlpha = weight / ( this.cumulativeWeight + weight );
            this.cumulativeValue = this.lerpValue( this.cumulativeValue, value, lerpAlpha );
            this.cumulativeWeight += weight;

        }

    },

    unbind: function() {

        if ( ! this.isBound ) return;

        this.setValue( this.originalValue );

        this.setValue = null;
        this.getValue = null;
        this.lerpValue = null;
        this.equalsValue = null;
        this.triggerDirty = null;
        this.isBound = false;

    },

    // bind to the real property in the scene graph, remember original value, memorize various accessors for speed/inefficiency
    bind: function() {

        if ( this.isBound ) return;

        var targetObject = this.node;

        // ensure there is a value node
        if ( ! targetObject ) {
            console.error( "  trying to update node for track: " + this.trackName + " but it wasn't found." );
            return;
        }

        if ( this.objectName ) {
            // special case were we need to reach deeper into the hierarchy to get the face materials....
            if ( this.objectName === "materials" ) {
                if ( ! targetObject.material ) {
                    console.error( '  can not bind to material as node does not have a material', this );
                    return;
                }
                if ( ! targetObject.material.materials ) {
                    console.error( '  can not bind to material.materials as node.material does not have a materials array', this );
                    return;
                }
                targetObject = targetObject.material.materials;
            } else if ( this.objectName === "bones" ) {
                if ( ! targetObject.skeleton ) {
                    console.error( '  can not bind to bones as node does not have a skeleton', this );
                    return;
                }
                // potential future optimization: skip this if propertyIndex is already an integer, and convert the integer string to a true integer.

                targetObject = targetObject.skeleton.bones;

                // support resolving morphTarget names into indices.
                for ( var i = 0; i < targetObject.length; i ++ ) {
                    if ( targetObject[i].name === this.objectIndex ) {
                        this.objectIndex = i;
                        break;
                    }
                }
            } else {

                if ( targetObject[ this.objectName ] === undefined ) {
                    console.error( '  can not bind to objectName of node, undefined', this );
                    return;
                }
                targetObject = targetObject[ this.objectName ];
            }

            if ( this.objectIndex !== undefined ) {
                if ( targetObject[ this.objectIndex ] === undefined ) {
                    console.error( "  trying to bind to objectIndex of objectName, but is undefined:", this, targetObject );
                    return;
                }

                targetObject = targetObject[ this.objectIndex ];
            }

        }

        // special case mappings
        var nodeProperty = targetObject[ this.propertyName ];
        if ( ! nodeProperty ) {
            console.error( "  trying to update property for track: " + this.nodeName + '.' + this.propertyName + " but it wasn't found.", targetObject );
            return;
        }

        // access a sub element of the property array (only primitives are supported right now)
        if ( this.propertyIndex !== undefined ) {

            if ( this.propertyName === "morphTargetInfluences" ) {
                // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.

                // support resolving morphTarget names into indices.
                if ( ! targetObject.geometry ) {
                    console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry', this );
                }
                if ( ! targetObject.geometry.morphTargets ) {
                    console.error( '  can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this );
                }

                for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) {
                    if ( targetObject.geometry.morphTargets[i].name === this.propertyIndex ) {
                        this.propertyIndex = i;
                        break;
                    }
                }
            }

            this.setValue = function setValue_propertyIndexed( value ) {
                if ( ! this.equalsValue( nodeProperty[ this.propertyIndex ], value ) ) {
                    nodeProperty[ this.propertyIndex ] = value;
                    return true;
                }
                return false;
            };

            this.getValue = function getValue_propertyIndexed() {
                return nodeProperty[ this.propertyIndex ];
            };

        }
        // must use copy for Object3D.Euler/Quaternion
        else if ( nodeProperty.copy ) {

            this.setValue = function setValue_propertyObject( value ) {
                if ( ! this.equalsValue( nodeProperty, value ) ) {
                    nodeProperty.copy( value );
                    return true;
                }
                return false;
            }

            this.getValue = function getValue_propertyObject() {
                return nodeProperty;
            };

        }
        // otherwise just set the property directly on the node (do not use nodeProperty as it may not be a reference object)
        else {

            this.setValue = function setValue_property( value ) {
                if ( ! this.equalsValue( targetObject[ this.propertyName ], value ) ) {
                    targetObject[ this.propertyName ] = value;
                    return true;
                }
                return false;
            }

            this.getValue = function getValue_property() {
                return targetObject[ this.propertyName ];
            };

        }

        // trigger node dirty
        if ( targetObject.needsUpdate !== undefined ) { // material

            this.triggerDirty = function triggerDirty_needsUpdate() {
                this.node.needsUpdate = true;
            }

        } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform

            this.triggerDirty = function triggerDirty_matrixWorldNeedsUpdate() {
                targetObject.matrixWorldNeedsUpdate = true;
            }

        }

        this.originalValue = this.getValue();

        this.equalsValue = THREE.AnimationUtils.getEqualsFunc( this.originalValue );
        this.lerpValue = THREE.AnimationUtils.getLerpFunc( this.originalValue, true );

        this.isBound = true;

    },

    apply: function() {

        // for speed capture the setter pattern as a closure (sort of a memoization pattern: https://en.wikipedia.org/wiki/Memoization)
        if ( ! this.isBound ) this.bind();

        // early exit if there is nothing to apply.
        if ( this.cumulativeWeight > 0 ) {

            // blend with original value
            if ( this.cumulativeWeight < 1 ) {

                var remainingWeight = 1 - this.cumulativeWeight;
                var lerpAlpha = remainingWeight / ( this.cumulativeWeight + remainingWeight );
                this.cumulativeValue = this.lerpValue( this.cumulativeValue, this.originalValue, lerpAlpha );

            }

            var valueChanged = this.setValue( this.cumulativeValue );

            if ( valueChanged && this.triggerDirty ) {
                this.triggerDirty();
            }

            // reset accumulator
            this.cumulativeValue = null;
            this.cumulativeWeight = 0;

        }
    }

};


THREE.PropertyBinding.parseTrackName = function( trackName ) {

    // matches strings in the form of:
    //    nodeName.property
    //    nodeName.property[accessor]
    //    nodeName.material.property[accessor]
    //    uuid.property[accessor]
    //    uuid.objectName[objectIndex].propertyName[propertyIndex]
    //    parentName/nodeName.property
    //    parentName/parentName/nodeName.property[index]
    //	  .bone[Armature.DEF_cog].position
    // created and tested via https://regex101.com/#javascript

    var re = /^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/;
    var matches = re.exec(trackName);

    if ( ! matches ) {
        throw new Error( "cannot parse trackName at all: " + trackName );
    }

    if (matches.index === re.lastIndex) {
        re.lastIndex++;
    }

    var results = {
        directoryName: matches[1],
        nodeName: matches[3], 	// allowed to be null, specified root node.
        objectName: matches[5],
        objectIndex: matches[7],
        propertyName: matches[9],
        propertyIndex: matches[11]	// allowed to be null, specifies that the whole property is set.
    };

    if ( results.propertyName === null || results.propertyName.length === 0 ) {
        throw new Error( "can not parse propertyName from trackName: " + trackName );
    }

    return results;

};

THREE.PropertyBinding.findNode = function( root, nodeName ) {

    function searchSkeleton( skeleton ) {

        for ( var i = 0; i < skeleton.bones.length; i ++ ) {

            var bone = skeleton.bones[i];

            if ( bone.name === nodeName ) {

                return bone;

            }
        }

        return null;

    }

    function searchNodeSubtree( children ) {

        for ( var i = 0; i < children.length; i ++ ) {

            var childNode = children[i];

            if ( childNode.name === nodeName || childNode.uuid === nodeName ) {

                return childNode;

            }

            var result = searchNodeSubtree( childNode.children );

            if ( result ) return result;

        }

        return null;

    }

    //

    if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {

        return root;

    }

    // search into skeleton bones.
    if ( root.skeleton ) {

        var bone = searchSkeleton( root.skeleton );

        if ( bone ) {

            return bone;

        }
    }

    // search into node subtree.
    if ( root.children ) {

        var subTreeNode = searchNodeSubtree( root.children );

        if ( subTreeNode ) {

            return subTreeNode;

        }

    }

    return null;
}

// File:src/animation/tracks/VectorKeyframeTrack.js

/**
 *
 * A Track that interpolates Vectors
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 */

THREE.VectorKeyframeTrack = function ( name, keys ) {

    THREE.KeyframeTrack.call( this, name, keys );

    // local cache of value type to avoid allocations during runtime.
    this.result = this.keys[0].value.clone();

};

THREE.VectorKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );

THREE.VectorKeyframeTrack.prototype.constructor = THREE.VectorKeyframeTrack;

THREE.VectorKeyframeTrack.prototype.setResult = function( value ) {

    this.result.copy( value );

};

// memoization of the lerp function for speed.
// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
THREE.VectorKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {

    return value0.lerp( value1, alpha );

};

THREE.VectorKeyframeTrack.prototype.compareValues = function( value0, value1 ) {

    return value0.equals( value1 );

};

THREE.VectorKeyframeTrack.prototype.clone = function() {

    var clonedKeys = [];

    for ( var i = 0; i < this.keys.length; i ++ ) {

        var key = this.keys[i];
        clonedKeys.push( {
            time: key.time,
            value: key.value.clone()
        } );
    }

    return new THREE.VectorKeyframeTrack( this.name, clonedKeys );

};

THREE.VectorKeyframeTrack.parse = function( json ) {

    var elementCount = json.keys[0].value.length;
    var valueType = THREE[ 'Vector' + elementCount ];

    var keys = [];

    for ( var i = 0; i < json.keys.length; i ++ ) {
        var jsonKey = json.keys[i];
        keys.push( {
            value: new valueType().fromArray( jsonKey.value ),
            time: jsonKey.time
        } );
    }

    return new THREE.VectorKeyframeTrack( json.name, keys );

};

// File:src/animation/tracks/QuaternionKeyframeTrack.js

/**
 *
 * A Track that interpolates Quaternion
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 */

THREE.QuaternionKeyframeTrack = function ( name, keys ) {

    THREE.KeyframeTrack.call( this, name, keys );

    // local cache of value type to avoid allocations during runtime.
    this.result = this.keys[0].value.clone();

};

THREE.QuaternionKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );

THREE.QuaternionKeyframeTrack.prototype.constructor = THREE.QuaternionKeyframeTrack;

THREE.QuaternionKeyframeTrack.prototype.setResult = function( value ) {

    this.result.copy( value );

};

// memoization of the lerp function for speed.
// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
THREE.QuaternionKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {

    return value0.slerp( value1, alpha );

};

THREE.QuaternionKeyframeTrack.prototype.compareValues = function( value0, value1 ) {

    return value0.equals( value1 );

};

THREE.QuaternionKeyframeTrack.prototype.multiply = function( quat ) {

    for ( var i = 0; i < this.keys.length; i ++ ) {

        this.keys[i].value.multiply( quat );

    }

    return this;

};

THREE.QuaternionKeyframeTrack.prototype.clone = function() {

    var clonedKeys = [];

    for ( var i = 0; i < this.keys.length; i ++ ) {

        var key = this.keys[i];
        clonedKeys.push( {
            time: key.time,
            value: key.value.clone()
        } );
    }

    return new THREE.QuaternionKeyframeTrack( this.name, clonedKeys );

};

THREE.QuaternionKeyframeTrack.parse = function( json ) {

    var keys = [];

    for ( var i = 0; i < json.keys.length; i ++ ) {
        var jsonKey = json.keys[i];
        keys.push( {
            value: new THREE.Quaternion().fromArray( jsonKey.value ),
            time: jsonKey.time
        } );
    }

    return new THREE.QuaternionKeyframeTrack( json.name, keys );

};

// File:src/animation/tracks/StringKeyframeTrack.js

/**
 *
 * A Track that interpolates Strings
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 */

THREE.StringKeyframeTrack = function ( name, keys ) {

    THREE.KeyframeTrack.call( this, name, keys );

    // local cache of value type to avoid allocations during runtime.
    this.result = this.keys[0].value;

};

THREE.StringKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );

THREE.StringKeyframeTrack.prototype.constructor = THREE.StringKeyframeTrack;

THREE.StringKeyframeTrack.prototype.setResult = function( value ) {

    this.result = value;

};

// memoization of the lerp function for speed.
// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
THREE.StringKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {

    return ( alpha < 1.0 ) ? value0 : value1;

};

THREE.StringKeyframeTrack.prototype.compareValues = function( value0, value1 ) {

    return ( value0 === value1 );

};

THREE.StringKeyframeTrack.prototype.clone = function() {

    var clonedKeys = [];

    for ( var i = 0; i < this.keys.length; i ++ ) {

        var key = this.keys[i];
        clonedKeys.push( {
            time: key.time,
            value: key.value
        } );
    }

    return new THREE.StringKeyframeTrack( this.name, clonedKeys );

};

THREE.StringKeyframeTrack.parse = function( json ) {

    return new THREE.StringKeyframeTrack( json.name, json.keys );

};

// File:src/animation/tracks/BooleanKeyframeTrack.js

/**
 *
 * A Track that interpolates Boolean
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 */

THREE.BooleanKeyframeTrack = function ( name, keys ) {

    THREE.KeyframeTrack.call( this, name, keys );

    // local cache of value type to avoid allocations during runtime.
    this.result = this.keys[0].value;

};

THREE.BooleanKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );

THREE.BooleanKeyframeTrack.prototype.constructor = THREE.BooleanKeyframeTrack;

THREE.BooleanKeyframeTrack.prototype.setResult = function( value ) {

    this.result = value;

};

// memoization of the lerp function for speed.
// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
THREE.BooleanKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {

    return ( alpha < 1.0 ) ? value0 : value1;

};

THREE.BooleanKeyframeTrack.prototype.compareValues = function( value0, value1 ) {

    return ( value0 === value1 );

};

THREE.BooleanKeyframeTrack.prototype.clone = function() {

    var clonedKeys = [];

    for ( var i = 0; i < this.keys.length; i ++ ) {

        var key = this.keys[i];
        clonedKeys.push( {
            time: key.time,
            value: key.value
        } );
    }

    return new THREE.BooleanKeyframeTrack( this.name, clonedKeys );

};

THREE.BooleanKeyframeTrack.parse = function( json ) {

    return new THREE.BooleanKeyframeTrack( json.name, json.keys );

};

// File:src/animation/tracks/NumberKeyframeTrack.js

/**
 *
 * A Track that interpolates Numbers
 *
 * @author Ben Houston / http://clara.io/
 * @author David Sarno / http://lighthaus.us/
 */

THREE.NumberKeyframeTrack = function ( name, keys ) {

    THREE.KeyframeTrack.call( this, name, keys );

    // local cache of value type to avoid allocations during runtime.
    this.result = this.keys[0].value;

};

THREE.NumberKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype );

THREE.NumberKeyframeTrack.prototype.constructor = THREE.NumberKeyframeTrack;

THREE.NumberKeyframeTrack.prototype.setResult = function( value ) {

    this.result = value;

};

// memoization of the lerp function for speed.
// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis.
THREE.NumberKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) {

    return value0 * ( 1 - alpha ) + value1 * alpha;

};

THREE.NumberKeyframeTrack.prototype.compareValues = function( value0, value1 ) {

    return ( value0 === value1 );

};

THREE.NumberKeyframeTrack.prototype.clone = function() {

    var clonedKeys = [];

    for ( var i = 0; i < this.keys.length; i ++ ) {

        var key = this.keys[i];
        clonedKeys.push( {
            time: key.time,
            value: key.value
        } );
    }

    return new THREE.NumberKeyframeTrack( this.name, clonedKeys );

};

THREE.NumberKeyframeTrack.parse = function( json ) {

    return new THREE.NumberKeyframeTrack( json.name, json.keys );

};

// File:src/cameras/Camera.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author mikael emtinger / http://gomo.se/
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.Camera = function () {

    THREE.Object3D.call( this );

    this.type = 'Camera';

    this.matrixWorldInverse = new THREE.Matrix4();
    this.projectionMatrix = new THREE.Matrix4();

};

THREE.Camera.prototype = Object.create( THREE.Object3D.prototype );
THREE.Camera.prototype.constructor = THREE.Camera;

THREE.Camera.prototype.getWorldDirection = function () {

    var quaternion = new THREE.Quaternion();

    return function ( optionalTarget ) {

        var result = optionalTarget || new THREE.Vector3();

        this.getWorldQuaternion( quaternion );

        return result.set( 0, 0, - 1 ).applyQuaternion( quaternion );

    };

}();

THREE.Camera.prototype.lookAt = function () {

    // This routine does not support cameras with rotated and/or translated parent(s)

    var m1 = new THREE.Matrix4();

    return function ( vector ) {

        m1.lookAt( this.position, vector, this.up );

        this.quaternion.setFromRotationMatrix( m1 );

    };

}();

THREE.Camera.prototype.clone = function () {

    return new this.constructor().copy( this );

};

THREE.Camera.prototype.copy = function ( source ) {

    THREE.Object3D.prototype.copy.call( this, source );

    this.matrixWorldInverse.copy( source.matrixWorldInverse );
    this.projectionMatrix.copy( source.projectionMatrix );

    return this;

};

// File:src/cameras/CubeCamera.js

/**
 * Camera for rendering cube maps
 *	- renders scene into axis-aligned cube
 *
 * @author alteredq / http://alteredqualia.com/
 */

THREE.CubeCamera = function ( near, far, cubeResolution ) {

    THREE.Object3D.call( this );

    this.type = 'CubeCamera';

    var fov = 90, aspect = 1;

    var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far );
    cameraPX.up.set( 0, - 1, 0 );
    cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) );
    this.add( cameraPX );

    var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far );
    cameraNX.up.set( 0, - 1, 0 );
    cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) );
    this.add( cameraNX );

    var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far );
    cameraPY.up.set( 0, 0, 1 );
    cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) );
    this.add( cameraPY );

    var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far );
    cameraNY.up.set( 0, 0, - 1 );
    cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) );
    this.add( cameraNY );

    var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
    cameraPZ.up.set( 0, - 1, 0 );
    cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) );
    this.add( cameraPZ );

    var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
    cameraNZ.up.set( 0, - 1, 0 );
    cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) );
    this.add( cameraNZ );

    this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } );

    this.updateCubeMap = function ( renderer, scene ) {

        if ( this.parent === null ) this.updateMatrixWorld();

        var renderTarget = this.renderTarget;
        var generateMipmaps = renderTarget.texture.generateMipmaps;

        renderTarget.texture.generateMipmaps = false;

        renderTarget.activeCubeFace = 0;
        renderer.render( scene, cameraPX, renderTarget );

        renderTarget.activeCubeFace = 1;
        renderer.render( scene, cameraNX, renderTarget );

        renderTarget.activeCubeFace = 2;
        renderer.render( scene, cameraPY, renderTarget );

        renderTarget.activeCubeFace = 3;
        renderer.render( scene, cameraNY, renderTarget );

        renderTarget.activeCubeFace = 4;
        renderer.render( scene, cameraPZ, renderTarget );

        renderTarget.texture.generateMipmaps = generateMipmaps;

        renderTarget.activeCubeFace = 5;
        renderer.render( scene, cameraNZ, renderTarget );

        renderer.setRenderTarget( null );

    };

};

THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype );
THREE.CubeCamera.prototype.constructor = THREE.CubeCamera;

// File:src/cameras/OrthographicCamera.js

/**
 * @author alteredq / http://alteredqualia.com/
 */

THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) {

    THREE.Camera.call( this );

    this.type = 'OrthographicCamera';

    this.zoom = 1;

    this.left = left;
    this.right = right;
    this.top = top;
    this.bottom = bottom;

    this.near = ( near !== undefined ) ? near : 0.1;
    this.far = ( far !== undefined ) ? far : 2000;

    this.updateProjectionMatrix();

};

THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype );
THREE.OrthographicCamera.prototype.constructor = THREE.OrthographicCamera;

THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () {

    var dx = ( this.right - this.left ) / ( 2 * this.zoom );
    var dy = ( this.top - this.bottom ) / ( 2 * this.zoom );
    var cx = ( this.right + this.left ) / 2;
    var cy = ( this.top + this.bottom ) / 2;

    this.projectionMatrix.makeOrthographic( cx - dx, cx + dx, cy + dy, cy - dy, this.near, this.far );

};

THREE.OrthographicCamera.prototype.copy = function ( source ) {

    THREE.Camera.prototype.copy.call( this, source );

    this.left = source.left;
    this.right = source.right;
    this.top = source.top;
    this.bottom = source.bottom;
    this.near = source.near;
    this.far = source.far;

    this.zoom = source.zoom;

    return this;

};

THREE.OrthographicCamera.prototype.toJSON = function ( meta ) {

    var data = THREE.Object3D.prototype.toJSON.call( this, meta );

    data.object.zoom = this.zoom;
    data.object.left = this.left;
    data.object.right = this.right;
    data.object.top = this.top;
    data.object.bottom = this.bottom;
    data.object.near = this.near;
    data.object.far = this.far;

    return data;

};

// File:src/cameras/PerspectiveCamera.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author greggman / http://games.greggman.com/
 * @author zz85 / http://www.lab4games.net/zz85/blog
 */

THREE.PerspectiveCamera = function ( fov, aspect, near, far ) {

    THREE.Camera.call( this );

    this.type = 'PerspectiveCamera';

    this.zoom = 1;

    this.fov = fov !== undefined ? fov : 50;
    this.aspect = aspect !== undefined ? aspect : 1;
    this.near = near !== undefined ? near : 0.1;
    this.far = far !== undefined ? far : 2000;

    this.updateProjectionMatrix();

};

THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype );
THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera;


/**
 * Uses Focal Length (in mm) to estimate and set FOV
 * 35mm (full-frame) camera is used if frame size is not specified;
 * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
 */

THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) {

    if ( frameHeight === undefined ) frameHeight = 24;

    this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
    this.updateProjectionMatrix();

};


/**
 * Sets an offset in a larger frustum. This is useful for multi-window or
 * multi-monitor/multi-machine setups.
 *
 * For example, if you have 3x2 monitors and each monitor is 1920x1080 and
 * the monitors are in grid like this
 *
 *   +---+---+---+
 *   | A | B | C |
 *   +---+---+---+
 *   | D | E | F |
 *   +---+---+---+
 *
 * then for each monitor you would call it like this
 *
 *   var w = 1920;
 *   var h = 1080;
 *   var fullWidth = w * 3;
 *   var fullHeight = h * 2;
 *
 *   --A--
 *   camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
 *   --B--
 *   camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
 *   --C--
 *   camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
 *   --D--
 *   camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
 *   --E--
 *   camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
 *   --F--
 *   camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
 *
 *   Note there is no reason monitors have to be the same size or in a grid.
 */

THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) {

    this.fullWidth = fullWidth;
    this.fullHeight = fullHeight;
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;

    this.updateProjectionMatrix();

};


THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () {

    var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) );

    if ( this.fullWidth ) {

        var aspect = this.fullWidth / this.fullHeight;
        var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near;
        var bottom = - top;
        var left = aspect * bottom;
        var right = aspect * top;
        var width = Math.abs( right - left );
        var height = Math.abs( top - bottom );

        this.projectionMatrix.makeFrustum(
            left + this.x * width / this.fullWidth,
            left + ( this.x + this.width ) * width / this.fullWidth,
            top - ( this.y + this.height ) * height / this.fullHeight,
            top - this.y * height / this.fullHeight,
            this.near,
            this.far
        );

    } else {

        this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far );

    }

};

THREE.PerspectiveCamera.prototype.copy = function ( source ) {

    THREE.Camera.prototype.copy.call( this, source );

    this.fov = source.fov;
    this.aspect = source.aspect;
    this.near = source.near;
    this.far = source.far;

    this.zoom = source.zoom;

    return this;

};

THREE.PerspectiveCamera.prototype.toJSON = function ( meta ) {

    var data = THREE.Object3D.prototype.toJSON.call( this, meta );

    data.object.zoom = this.zoom;
    data.object.fov = this.fov;
    data.object.aspect = this.aspect;
    data.object.near = this.near;
    data.object.far = this.far;

    return data;

};

// File:src/lights/Light.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Light = function ( color ) {

    THREE.Object3D.call( this );

    this.type = 'Light';

    this.color = new THREE.Color( color );

    this.receiveShadow = undefined;

};

THREE.Light.prototype = Object.create( THREE.Object3D.prototype );
THREE.Light.prototype.constructor = THREE.Light;

Object.defineProperties( THREE.Light.prototype, {
    onlyShadow: {
        set: function ( value ) {
            console.warn( 'THREE.Light: .onlyShadow has been removed.' );
        }
    },
    shadowCameraFov: {
        set: function ( value ) {
            this.shadow.camera.fov = value;
        }
    },
    shadowCameraLeft: {
        set: function ( value ) {
            this.shadow.camera.left = value;
        }
    },
    shadowCameraRight: {
        set: function ( value ) {
            this.shadow.camera.right = value;
        }
    },
    shadowCameraTop: {
        set: function ( value ) {
            this.shadow.camera.top = value;
        }
    },
    shadowCameraBottom: {
        set: function ( value ) {
            this.shadow.camera.bottom = value;
        }
    },
    shadowCameraNear: {
        set: function ( value ) {
            this.shadow.camera.near = value;
        }
    },
    shadowCameraFar: {
        set: function ( value ) {
            this.shadow.camera.far = value;
        }
    },
    shadowCameraVisible: {
        set: function ( value ) {
            console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow ) instead.' );
        }
    },
    shadowBias: {
        set: function ( value ) {
            this.shadow.bias = value;
        }
    },
    shadowDarkness: {
        set: function ( value ) {
            this.shadow.darkness = value;
        }
    },
    shadowMapWidth: {
        set: function ( value ) {
            this.shadow.mapSize.width = value;
        }
    },
    shadowMapHeight: {
        set: function ( value ) {
            this.shadow.mapSize.height = value;
        }
    }
} );

THREE.Light.prototype.copy = function ( source ) {

    THREE.Object3D.prototype.copy.call( this, source );

    this.color.copy( source.color );

    return this;

};

THREE.Light.prototype.toJSON = function ( meta ) {

    var data = THREE.Object3D.prototype.toJSON.call( this, meta );

    data.object.color = this.color.getHex();
    if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex();

    if ( this.intensity !== undefined ) data.object.intensity = this.intensity;
    if ( this.distance !== undefined ) data.object.distance = this.distance;
    if ( this.angle !== undefined ) data.object.angle = this.angle;
    if ( this.decay !== undefined ) data.object.decay = this.decay;
    if ( this.exponent !== undefined ) data.object.exponent = this.exponent;

    return data;

};

// File:src/lights/LightShadow.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.LightShadow = function ( camera ) {

    this.camera = camera;

    this.bias = 0;
    this.darkness = 1;

    this.mapSize = new THREE.Vector2( 512, 512 );

    this.map = null;
    this.matrix = null;

};

THREE.LightShadow.prototype = {

    constructor: THREE.LightShadow,

    copy: function ( source ) {

        this.camera = source.camera.clone();

        this.bias = source.bias;
        this.darkness = source.darkness;

        this.mapSize.copy( source.mapSize );

    },

    clone: function () {

        return new this.constructor().copy( this );

    }

};

// File:src/lights/AmbientLight.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.AmbientLight = function ( color ) {

    THREE.Light.call( this, color );

    this.type = 'AmbientLight';

    this.castShadow = undefined;

};

THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype );
THREE.AmbientLight.prototype.constructor = THREE.AmbientLight;

// File:src/lights/DirectionalLight.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.DirectionalLight = function ( color, intensity ) {

    THREE.Light.call( this, color );

    this.type = 'DirectionalLight';

    this.position.set( 0, 1, 0 );
    this.updateMatrix();

    this.target = new THREE.Object3D();

    this.intensity = ( intensity !== undefined ) ? intensity : 1;

    this.shadow = new THREE.LightShadow( new THREE.OrthographicCamera( - 500, 500, 500, - 500, 50, 5000 ) );

};

THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype );
THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight;

THREE.DirectionalLight.prototype.copy = function ( source ) {

    THREE.Light.prototype.copy.call( this, source );

    this.intensity = source.intensity;
    this.target = source.target.clone();

    this.shadow = source.shadow.clone();

    return this;

};

// File:src/lights/HemisphereLight.js

/**
 * @author alteredq / http://alteredqualia.com/
 */

THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) {

    THREE.Light.call( this, skyColor );

    this.type = 'HemisphereLight';

    this.castShadow = undefined;

    this.position.set( 0, 1, 0 );
    this.updateMatrix();

    this.groundColor = new THREE.Color( groundColor );
    this.intensity = ( intensity !== undefined ) ? intensity : 1;

};

THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype );
THREE.HemisphereLight.prototype.constructor = THREE.HemisphereLight;

THREE.HemisphereLight.prototype.copy = function ( source ) {

    THREE.Light.prototype.copy.call( this, source );

    this.groundColor.copy( source.groundColor );
    this.intensity = source.intensity;

    return this;

};

// File:src/lights/PointLight.js

/**
 * @author mrdoob / http://mrdoob.com/
 */


THREE.PointLight = function ( color, intensity, distance, decay ) {

    THREE.Light.call( this, color );

    this.type = 'PointLight';

    this.intensity = ( intensity !== undefined ) ? intensity : 1;
    this.distance = ( distance !== undefined ) ? distance : 0;
    this.decay = ( decay !== undefined ) ? decay : 1;	// for physically correct lights, should be 2.

    this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 90, 1, 1, 500 ) );

};

THREE.PointLight.prototype = Object.create( THREE.Light.prototype );
THREE.PointLight.prototype.constructor = THREE.PointLight;

THREE.PointLight.prototype.copy = function ( source ) {

    THREE.Light.prototype.copy.call( this, source );

    this.intensity = source.intensity;
    this.distance = source.distance;
    this.decay = source.decay;

    this.shadow = source.shadow.clone();

    return this;

};

// File:src/lights/SpotLight.js

/**
 * @author alteredq / http://alteredqualia.com/
 */

THREE.SpotLight = function ( color, intensity, distance, angle, exponent, decay ) {

    THREE.Light.call( this, color );

    this.type = 'SpotLight';

    this.position.set( 0, 1, 0 );
    this.updateMatrix();

    this.target = new THREE.Object3D();

    this.intensity = ( intensity !== undefined ) ? intensity : 1;
    this.distance = ( distance !== undefined ) ? distance : 0;
    this.angle = ( angle !== undefined ) ? angle : Math.PI / 3;
    this.exponent = ( exponent !== undefined ) ? exponent : 10;
    this.decay = ( decay !== undefined ) ? decay : 1;	// for physically correct lights, should be 2.

    this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 50, 1, 50, 5000 ) );

};

THREE.SpotLight.prototype = Object.create( THREE.Light.prototype );
THREE.SpotLight.prototype.constructor = THREE.SpotLight;

THREE.SpotLight.prototype.copy = function ( source ) {

    THREE.Light.prototype.copy.call( this, source );

    this.intensity = source.intensity;
    this.distance = source.distance;
    this.angle = source.angle;
    this.exponent = source.exponent;
    this.decay = source.decay;

    this.target = source.target.clone();

    this.shadow = source.shadow.clone();

    return this;

};

// File:src/loaders/Cache.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.Cache = {

    enabled: false,

    files: {},

    add: function ( key, file ) {

        if ( this.enabled === false ) return;

        // console.log( 'THREE.Cache', 'Adding key:', key );

        this.files[ key ] = file;

    },

    get: function ( key ) {

        if ( this.enabled === false ) return;

        // console.log( 'THREE.Cache', 'Checking key:', key );

        return this.files[ key ];

    },

    remove: function ( key ) {

        delete this.files[ key ];

    },

    clear: function () {

        this.files = {};

    }

};

// File:src/loaders/Loader.js

/**
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Loader = function () {

    this.onLoadStart = function () {};
    this.onLoadProgress = function () {};
    this.onLoadComplete = function () {};

};

THREE.Loader.prototype = {

    constructor: THREE.Loader,

    crossOrigin: undefined,

    extractUrlBase: function ( url ) {

        var parts = url.split( '/' );

        if ( parts.length === 1 ) return './';

        parts.pop();

        return parts.join( '/' ) + '/';

    },

    initMaterials: function ( materials, texturePath, crossOrigin ) {

        var array = [];

        for ( var i = 0; i < materials.length; ++ i ) {

            array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin );

        }

        return array;

    },

    createMaterial: ( function () {

        var color, textureLoader, materialLoader;

        return function ( m, texturePath, crossOrigin ) {

            if ( color === undefined ) color = new THREE.Color();
            if ( textureLoader === undefined ) textureLoader = new THREE.TextureLoader();
            if ( materialLoader === undefined ) materialLoader = new THREE.MaterialLoader();

            // convert from old material format

            var textures = {};

            function loadTexture( path, repeat, offset, wrap, anisotropy ) {

                var fullPath = texturePath + path;
                var loader = THREE.Loader.Handlers.get( fullPath );

                var texture;

                if ( loader !== null ) {

                    texture = loader.load( fullPath );

                } else {

                    textureLoader.setCrossOrigin( crossOrigin );
                    texture = textureLoader.load( fullPath );

                }

                if ( repeat !== undefined ) {

                    texture.repeat.fromArray( repeat );

                    if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping;
                    if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping;

                }

                if ( offset !== undefined ) {

                    texture.offset.fromArray( offset );

                }

                if ( wrap !== undefined ) {

                    if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = THREE.RepeatWrapping;
                    if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = THREE.MirroredRepeatWrapping;

                    if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = THREE.RepeatWrapping;
                    if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = THREE.MirroredRepeatWrapping;

                }

                if ( anisotropy !== undefined ) {

                    texture.anisotropy = anisotropy;

                }

                var uuid = THREE.Math.generateUUID();

                textures[ uuid ] = texture;

                return uuid;

            }

            //

            var json = {
                uuid: THREE.Math.generateUUID(),
                type: 'MeshLambertMaterial'
            };

            for ( var name in m ) {

                var value = m[ name ];

                switch ( name ) {
                    case 'DbgColor':
                        json.color = value;
                        break;
                    case 'DbgIndex':
                    case 'opticalDensity':
                    case 'illumination':
                        // These were never supported
                        break;
                    case 'DbgName':
                        json.name = value;
                        break;
                    case 'blending':
                        json.blending = THREE[ value ];
                        break;
                    case 'colorDiffuse':
                        json.color = color.fromArray( value ).getHex();
                        break;
                    case 'colorSpecular':
                        json.specular = color.fromArray( value ).getHex();
                        break;
                    case 'colorEmissive':
                        json.emissive = color.fromArray( value ).getHex();
                        break;
                    case 'specularCoef':
                        json.shininess = value;
                        break;
                    case 'shading':
                        if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial';
                        if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial';
                        break;
                    case 'mapDiffuse':
                        json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy );
                        break;
                    case 'mapDiffuseRepeat':
                    case 'mapDiffuseOffset':
                    case 'mapDiffuseWrap':
                    case 'mapDiffuseAnisotropy':
                        break;
                    case 'mapLight':
                        json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy );
                        break;
                    case 'mapLightRepeat':
                    case 'mapLightOffset':
                    case 'mapLightWrap':
                    case 'mapLightAnisotropy':
                        break;
                    case 'mapAO':
                        json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy );
                        break;
                    case 'mapAORepeat':
                    case 'mapAOOffset':
                    case 'mapAOWrap':
                    case 'mapAOAnisotropy':
                        break;
                    case 'mapBump':
                        json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy );
                        break;
                    case 'mapBumpScale':
                        json.bumpScale = value;
                        break;
                    case 'mapBumpRepeat':
                    case 'mapBumpOffset':
                    case 'mapBumpWrap':
                    case 'mapBumpAnisotropy':
                        break;
                    case 'mapNormal':
                        json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy );
                        break;
                    case 'mapNormalFactor':
                        json.normalScale = [ value, value ];
                        break;
                    case 'mapNormalRepeat':
                    case 'mapNormalOffset':
                    case 'mapNormalWrap':
                    case 'mapNormalAnisotropy':
                        break;
                    case 'mapSpecular':
                        json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy );
                        break;
                    case 'mapSpecularRepeat':
                    case 'mapSpecularOffset':
                    case 'mapSpecularWrap':
                    case 'mapSpecularAnisotropy':
                        break;
                    case 'mapAlpha':
                        json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy );
                        break;
                    case 'mapAlphaRepeat':
                    case 'mapAlphaOffset':
                    case 'mapAlphaWrap':
                    case 'mapAlphaAnisotropy':
                        break;
                    case 'flipSided':
                        json.side = THREE.BackSide;
                        break;
                    case 'doubleSided':
                        json.side = THREE.DoubleSide;
                        break;
                    case 'transparency':
                        console.warn( 'THREE.Loader: transparency has been renamed to opacity' );
                        json.opacity = value;
                        break;
                    case 'opacity':
                    case 'transparent':
                    case 'depthTest':
                    case 'depthWrite':
                    case 'transparent':
                    case 'visible':
                    case 'wireframe':
                        json[ name ] = value;
                        break;
                    case 'vertexColors':
                        if ( value === true ) json.vertexColors = THREE.VertexColors;
                        if ( value === 'face' ) json.vertexColors = THREE.FaceColors;
                        break;
                    default:
                        console.error( 'Loader.createMaterial: Unsupported', name, value );
                        break;
                }

            }

            if ( json.type !== 'MeshPhongMaterial' ) delete json.specular;
            if ( json.opacity < 1 ) json.transparent = true;

            materialLoader.setTextures( textures );

            return materialLoader.parse( json );

        };

    } )()

};

THREE.Loader.Handlers = {

    handlers: [],

    add: function ( regex, loader ) {

        this.handlers.push( regex, loader );

    },

    get: function ( file ) {

        var handlers = this.handlers;

        for ( var i = 0, l = handlers.length; i < l; i += 2 ) {

            var regex = handlers[ i ];
            var loader  = handlers[ i + 1 ];

            if ( regex.test( file ) ) {

                return loader;

            }

        }

        return null;

    }

};

// File:src/loaders/XHRLoader.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.XHRLoader = function ( manager ) {

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.XHRLoader.prototype = {

    constructor: THREE.XHRLoader,

    load: function ( url, onLoad, onProgress, onError ) {

        var scope = this;

        var cached = THREE.Cache.get( url );

        if ( cached !== undefined ) {

            if ( onLoad ) {

                setTimeout( function () {

                    onLoad( cached );

                }, 0 );

            }

            return cached;

        }

        var request = new XMLHttpRequest();
        request.open( 'GET', url, true );

        request.addEventListener( 'load', function ( event ) {

            var response = event.target.response;

            THREE.Cache.add( url, response );

            if ( onLoad ) onLoad( response );

            scope.manager.itemEnd( url );

        }, false );

        if ( onProgress !== undefined ) {

            request.addEventListener( 'progress', function ( event ) {

                onProgress( event );

            }, false );

        }

        request.addEventListener( 'error', function ( event ) {

            if ( onError ) onError( event );

            scope.manager.itemError( url );

        }, false );

        if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin;
        if ( this.responseType !== undefined ) request.responseType = this.responseType;
        if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials;

        request.send( null );

        scope.manager.itemStart( url );

        return request;

    },

    setResponseType: function ( value ) {

        this.responseType = value;

    },

    setCrossOrigin: function ( value ) {

        this.crossOrigin = value;

    },

    setWithCredentials: function ( value ) {

        this.withCredentials = value;

    }

};

// File:src/loaders/ImageLoader.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.ImageLoader = function ( manager ) {

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.ImageLoader.prototype = {

    constructor: THREE.ImageLoader,

    load: function ( url, onLoad, onProgress, onError ) {

        var scope = this;

        var cached = THREE.Cache.get( url );

        if ( cached !== undefined ) {

            scope.manager.itemStart( url );

            if ( onLoad ) {

                setTimeout( function () {

                    onLoad( cached );

                    scope.manager.itemEnd( url );

                }, 0 );

            } else {

                scope.manager.itemEnd( url );

            }

            return cached;

        }

        var image = document.createElement( 'img' );

        image.addEventListener( 'load', function ( event ) {

            THREE.Cache.add( url, this );

            if ( onLoad ) onLoad( this );

            scope.manager.itemEnd( url );

        }, false );

        if ( onProgress !== undefined ) {

            image.addEventListener( 'progress', function ( event ) {

                onProgress( event );

            }, false );

        }

        image.addEventListener( 'error', function ( event ) {

            if ( onError ) onError( event );

            scope.manager.itemError( url );

        }, false );

        if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin;

        scope.manager.itemStart( url );

        image.src = url;

        return image;

    },

    setCrossOrigin: function ( value ) {

        this.crossOrigin = value;

    }

};

// File:src/loaders/JSONLoader.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.JSONLoader = function ( manager ) {

    if ( typeof manager === 'boolean' ) {

        console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' );
        manager = undefined;

    }

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

    this.withCredentials = false;

};

THREE.JSONLoader.prototype = {

    constructor: THREE.JSONLoader,

    // Deprecated

    get statusDomElement () {

        if ( this._statusDomElement === undefined ) {

            this._statusDomElement = document.createElement( 'div' );

        }

        console.warn( 'THREE.JSONLoader: .statusDomElement has been removed.' );
        return this._statusDomElement;

    },

    load: function( url, onLoad, onProgress, onError ) {

        var scope = this;

        var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : THREE.Loader.prototype.extractUrlBase( url );

        var loader = new THREE.XHRLoader( this.manager );
        loader.setCrossOrigin( this.crossOrigin );
        loader.setWithCredentials( this.withCredentials );
        loader.load( url, function ( text ) {

            var json = JSON.parse( text );
            var metadata = json.metadata;

            if ( metadata !== undefined ) {

                if ( metadata.type === 'object' ) {

                    console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' );
                    return;

                }

                if ( metadata.type === 'scene' ) {

                    console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' );
                    return;

                }

            }

            var object = scope.parse( json, texturePath );
            onLoad( object.geometry, object.materials );

        } );

    },

    setCrossOrigin: function ( value ) {

        this.crossOrigin = value;

    },

    setTexturePath: function ( value ) {

        this.texturePath = value;

    },

    parse: function ( json, texturePath ) {

        var geometry = new THREE.Geometry(),
            scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0;

        parseModel( scale );

        parseSkin();
        parseMorphing( scale );
        parseAnimations();

        geometry.computeFaceNormals();
        geometry.computeBoundingSphere();

        function parseModel( scale ) {

            function isBitSet( value, position ) {

                return value & ( 1 << position );

            }

            var i, j, fi,

                offset, zLength,

                colorIndex, normalIndex, uvIndex, materialIndex,

                type,
                isQuad,
                hasMaterial,
                hasFaceVertexUv,
                hasFaceNormal, hasFaceVertexNormal,
                hasFaceColor, hasFaceVertexColor,

                vertex, face, faceA, faceB, hex, normal,

                uvLayer, uv, u, v,

                faces = json.faces,
                vertices = json.vertices,
                normals = json.normals,
                colors = json.colors,

                nUvLayers = 0;

            if ( json.uvs !== undefined ) {

                // disregard empty arrays

                for ( i = 0; i < json.uvs.length; i ++ ) {

                    if ( json.uvs[ i ].length ) nUvLayers ++;

                }

                for ( i = 0; i < nUvLayers; i ++ ) {

                    geometry.faceVertexUvs[ i ] = [];

                }

            }

            offset = 0;
            zLength = vertices.length;

            while ( offset < zLength ) {

                vertex = new THREE.Vector3();

                vertex.x = vertices[ offset ++ ] * scale;
                vertex.y = vertices[ offset ++ ] * scale;
                vertex.z = vertices[ offset ++ ] * scale;

                geometry.vertices.push( vertex );

            }

            offset = 0;
            zLength = faces.length;

            while ( offset < zLength ) {

                type = faces[ offset ++ ];


                isQuad              = isBitSet( type, 0 );
                hasMaterial         = isBitSet( type, 1 );
                hasFaceVertexUv     = isBitSet( type, 3 );
                hasFaceNormal       = isBitSet( type, 4 );
                hasFaceVertexNormal = isBitSet( type, 5 );
                hasFaceColor	     = isBitSet( type, 6 );
                hasFaceVertexColor  = isBitSet( type, 7 );

                // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);

                if ( isQuad ) {

                    faceA = new THREE.Face3();
                    faceA.a = faces[ offset ];
                    faceA.b = faces[ offset + 1 ];
                    faceA.c = faces[ offset + 3 ];

                    faceB = new THREE.Face3();
                    faceB.a = faces[ offset + 1 ];
                    faceB.b = faces[ offset + 2 ];
                    faceB.c = faces[ offset + 3 ];

                    offset += 4;

                    if ( hasMaterial ) {

                        materialIndex = faces[ offset ++ ];
                        faceA.materialIndex = materialIndex;
                        faceB.materialIndex = materialIndex;

                    }

                    // to get face <=> uv index correspondence

                    fi = geometry.faces.length;

                    if ( hasFaceVertexUv ) {

                        for ( i = 0; i < nUvLayers; i ++ ) {

                            uvLayer = json.uvs[ i ];

                            geometry.faceVertexUvs[ i ][ fi ] = [];
                            geometry.faceVertexUvs[ i ][ fi + 1 ] = [];

                            for ( j = 0; j < 4; j ++ ) {

                                uvIndex = faces[ offset ++ ];

                                u = uvLayer[ uvIndex * 2 ];
                                v = uvLayer[ uvIndex * 2 + 1 ];

                                uv = new THREE.Vector2( u, v );

                                if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv );
                                if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv );

                            }

                        }

                    }

                    if ( hasFaceNormal ) {

                        normalIndex = faces[ offset ++ ] * 3;

                        faceA.normal.set(
                            normals[ normalIndex ++ ],
                            normals[ normalIndex ++ ],
                            normals[ normalIndex ]
                        );

                        faceB.normal.copy( faceA.normal );

                    }

                    if ( hasFaceVertexNormal ) {

                        for ( i = 0; i < 4; i ++ ) {

                            normalIndex = faces[ offset ++ ] * 3;

                            normal = new THREE.Vector3(
                                normals[ normalIndex ++ ],
                                normals[ normalIndex ++ ],
                                normals[ normalIndex ]
                            );


                            if ( i !== 2 ) faceA.vertexNormals.push( normal );
                            if ( i !== 0 ) faceB.vertexNormals.push( normal );

                        }

                    }


                    if ( hasFaceColor ) {

                        colorIndex = faces[ offset ++ ];
                        hex = colors[ colorIndex ];

                        faceA.color.setHex( hex );
                        faceB.color.setHex( hex );

                    }


                    if ( hasFaceVertexColor ) {

                        for ( i = 0; i < 4; i ++ ) {

                            colorIndex = faces[ offset ++ ];
                            hex = colors[ colorIndex ];

                            if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) );
                            if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) );

                        }

                    }

                    geometry.faces.push( faceA );
                    geometry.faces.push( faceB );

                } else {

                    face = new THREE.Face3();
                    face.a = faces[ offset ++ ];
                    face.b = faces[ offset ++ ];
                    face.c = faces[ offset ++ ];

                    if ( hasMaterial ) {

                        materialIndex = faces[ offset ++ ];
                        face.materialIndex = materialIndex;

                    }

                    // to get face <=> uv index correspondence

                    fi = geometry.faces.length;

                    if ( hasFaceVertexUv ) {

                        for ( i = 0; i < nUvLayers; i ++ ) {

                            uvLayer = json.uvs[ i ];

                            geometry.faceVertexUvs[ i ][ fi ] = [];

                            for ( j = 0; j < 3; j ++ ) {

                                uvIndex = faces[ offset ++ ];

                                u = uvLayer[ uvIndex * 2 ];
                                v = uvLayer[ uvIndex * 2 + 1 ];

                                uv = new THREE.Vector2( u, v );

                                geometry.faceVertexUvs[ i ][ fi ].push( uv );

                            }

                        }

                    }

                    if ( hasFaceNormal ) {

                        normalIndex = faces[ offset ++ ] * 3;

                        face.normal.set(
                            normals[ normalIndex ++ ],
                            normals[ normalIndex ++ ],
                            normals[ normalIndex ]
                        );

                    }

                    if ( hasFaceVertexNormal ) {

                        for ( i = 0; i < 3; i ++ ) {

                            normalIndex = faces[ offset ++ ] * 3;

                            normal = new THREE.Vector3(
                                normals[ normalIndex ++ ],
                                normals[ normalIndex ++ ],
                                normals[ normalIndex ]
                            );

                            face.vertexNormals.push( normal );

                        }

                    }


                    if ( hasFaceColor ) {

                        colorIndex = faces[ offset ++ ];
                        face.color.setHex( colors[ colorIndex ] );

                    }


                    if ( hasFaceVertexColor ) {

                        for ( i = 0; i < 3; i ++ ) {

                            colorIndex = faces[ offset ++ ];
                            face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) );

                        }

                    }

                    geometry.faces.push( face );

                }

            }

        };

        function parseSkin() {

            var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2;

            if ( json.skinWeights ) {

                for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) {

                    var x =                               json.skinWeights[ i ];
                    var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0;
                    var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0;
                    var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0;

                    geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) );

                }

            }

            if ( json.skinIndices ) {

                for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) {

                    var a =                               json.skinIndices[ i ];
                    var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0;
                    var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0;
                    var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0;

                    geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) );

                }

            }

            geometry.bones = json.bones;

            if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) {

                console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' +
                    geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' );

            }

        };

        function parseMorphing( scale ) {

            if ( json.morphTargets !== undefined ) {

                for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) {

                    geometry.morphTargets[ i ] = {};
                    geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;
                    geometry.morphTargets[ i ].vertices = [];

                    var dstVertices = geometry.morphTargets[ i ].vertices;
                    var srcVertices = json.morphTargets[ i ].vertices;

                    for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) {

                        var vertex = new THREE.Vector3();
                        vertex.x = srcVertices[ v ] * scale;
                        vertex.y = srcVertices[ v + 1 ] * scale;
                        vertex.z = srcVertices[ v + 2 ] * scale;

                        dstVertices.push( vertex );

                    }

                }

            }

            if ( json.morphColors !== undefined && json.morphColors.length > 0 ) {

                console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' );

                var faces = geometry.faces;
                var morphColors = json.morphColors[ 0 ].colors;

                for ( var i = 0, l = faces.length; i < l; i ++ ) {

                    faces[ i ].color.fromArray( morphColors, i * 3 );

                }

            }

        }

        function parseAnimations() {

            var outputAnimations = [];

            // parse old style Bone/Hierarchy animations
            var animations = [];
            if ( json.animation !== undefined ) {
                animations.push( json.animation );
            }
            if ( json.animations !== undefined ) {
                if ( json.animations.length ) {
                    animations = animations.concat( json.animations );
                } else {
                    animations.push( json.animations );
                }
            }

            for ( var i = 0; i < animations.length; i ++ ) {

                var clip = THREE.AnimationClip.parseAnimation( animations[i], geometry.bones );
                if ( clip ) outputAnimations.push( clip );

            }

            // parse implicit morph animations
            if ( geometry.morphTargets ) {

                // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary.
                var morphAnimationClips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 );
                outputAnimations = outputAnimations.concat( morphAnimationClips );

            }

            if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations;

        };

        if ( json.materials === undefined || json.materials.length === 0 ) {

            return { geometry: geometry };

        } else {

            var materials = THREE.Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin );

            return { geometry: geometry, materials: materials };

        }

    }

};

// File:src/loaders/LoadingManager.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.LoadingManager = function ( onLoad, onProgress, onError ) {

    var scope = this;

    var isLoading = false, itemsLoaded = 0, itemsTotal = 0;

    this.onStart = undefined;
    this.onLoad = onLoad;
    this.onProgress = onProgress;
    this.onError = onError;

    this.itemStart = function ( url ) {

        itemsTotal ++;

        if ( isLoading === false ) {

            if ( scope.onStart !== undefined ) {

                scope.onStart( url, itemsLoaded, itemsTotal );

            }

        }

        isLoading = true;

    };

    this.itemEnd = function ( url ) {

        itemsLoaded ++;

        if ( scope.onProgress !== undefined ) {

            scope.onProgress( url, itemsLoaded, itemsTotal );

        }

        if ( itemsLoaded === itemsTotal ) {

            isLoading = false;

            if ( scope.onLoad !== undefined ) {

                scope.onLoad();

            }

        }

    };

    this.itemError = function ( url ) {

        if ( scope.onError !== undefined ) {

            scope.onError( url );

        }

    };

};

THREE.DefaultLoadingManager = new THREE.LoadingManager();

// File:src/loaders/BufferGeometryLoader.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.BufferGeometryLoader = function ( manager ) {

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.BufferGeometryLoader.prototype = {

    constructor: THREE.BufferGeometryLoader,

    load: function ( url, onLoad, onProgress, onError ) {

        var scope = this;

        var loader = new THREE.XHRLoader( scope.manager );
        loader.setCrossOrigin( this.crossOrigin );
        loader.load( url, function ( text ) {

            onLoad( scope.parse( JSON.parse( text ) ) );

        }, onProgress, onError );

    },

    setCrossOrigin: function ( value ) {

        this.crossOrigin = value;

    },

    parse: function ( json ) {

        var geometry = new THREE.BufferGeometry();

        var index = json.data.index;

        if ( index !== undefined ) {

            var typedArray = new self[ index.type ]( index.array );
            geometry.setIndex( new THREE.BufferAttribute( typedArray, 1 ) );

        }

        var attributes = json.data.attributes;

        for ( var key in attributes ) {

            var attribute = attributes[ key ];
            var typedArray = new self[ attribute.type ]( attribute.array );

            geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) );

        }

        var groups = json.data.groups || json.data.drawcalls || json.data.offsets;

        if ( groups !== undefined ) {

            for ( var i = 0, n = groups.length; i !== n; ++ i ) {

                var group = groups[ i ];

                geometry.addGroup( group.start, group.count );

            }

        }

        var boundingSphere = json.data.boundingSphere;

        if ( boundingSphere !== undefined ) {

            var center = new THREE.Vector3();

            if ( boundingSphere.center !== undefined ) {

                center.fromArray( boundingSphere.center );

            }

            geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius );

        }

        return geometry;

    }

};

// File:src/loaders/MaterialLoader.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.MaterialLoader = function ( manager ) {

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
    this.textures = {};

};

THREE.MaterialLoader.prototype = {

    constructor: THREE.MaterialLoader,

    load: function ( url, onLoad, onProgress, onError ) {

        var scope = this;

        var loader = new THREE.XHRLoader( scope.manager );
        loader.setCrossOrigin( this.crossOrigin );
        loader.load( url, function ( text ) {

            onLoad( scope.parse( JSON.parse( text ) ) );

        }, onProgress, onError );

    },

    setCrossOrigin: function ( value ) {

        this.crossOrigin = value;

    },

    setTextures: function ( value ) {

        this.textures = value;

    },

    getTexture: function ( name ) {

        var textures = this.textures;

        if ( textures[ name ] === undefined ) {

            console.warn( 'THREE.MaterialLoader: Undefined texture', name );

        }

        return textures[ name ];

    },

    parse: function ( json ) {

        var material = new THREE[ json.type ];
        material.uuid = json.uuid;

        if ( json.name !== undefined ) material.name = json.name;
        if ( json.color !== undefined ) material.color.setHex( json.color );
        if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive );
        if ( json.specular !== undefined ) material.specular.setHex( json.specular );
        if ( json.shininess !== undefined ) material.shininess = json.shininess;
        if ( json.uniforms !== undefined ) material.uniforms = json.uniforms;
        if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;
        if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;
        if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;
        if ( json.shading !== undefined ) material.shading = json.shading;
        if ( json.blending !== undefined ) material.blending = json.blending;
        if ( json.side !== undefined ) material.side = json.side;
        if ( json.opacity !== undefined ) material.opacity = json.opacity;
        if ( json.transparent !== undefined ) material.transparent = json.transparent;
        if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest;
        if ( json.depthTest !== undefined ) material.depthTest = json.depthTest;
        if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite;
        if ( json.wireframe !== undefined ) material.wireframe = json.wireframe;
        if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;

        // for PointsMaterial
        if ( json.size !== undefined ) material.size = json.size;
        if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation;

        // maps

        if ( json.map !== undefined ) material.map = this.getTexture( json.map );

        if ( json.alphaMap !== undefined ) {

            material.alphaMap = this.getTexture( json.alphaMap );
            material.transparent = true;

        }

        if ( json.bumpMap !== undefined ) material.bumpMap = this.getTexture( json.bumpMap );
        if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale;

        if ( json.normalMap !== undefined ) material.normalMap = this.getTexture( json.normalMap );
        if ( json.normalScale )	material.normalScale = new THREE.Vector2( json.normalScale, json.normalScale );

        if ( json.displacementMap !== undefined ) material.displacementMap = this.getTexture( json.displacementMap );
        if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale;
        if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias;

        if ( json.specularMap !== undefined ) material.specularMap = this.getTexture( json.specularMap );

        if ( json.envMap !== undefined ) {

            material.envMap = this.getTexture( json.envMap );
            material.combine = THREE.MultiplyOperation;

        }

        if ( json.reflectivity ) material.reflectivity = json.reflectivity;

        if ( json.lightMap !== undefined ) material.lightMap = this.getTexture( json.lightMap );
        if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity;

        if ( json.aoMap !== undefined ) material.aoMap = this.getTexture( json.aoMap );
        if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;

        // MeshFaceMaterial

        if ( json.materials !== undefined ) {

            for ( var i = 0, l = json.materials.length; i < l; i ++ ) {

                material.materials.push( this.parse( json.materials[ i ] ) );

            }

        }

        return material;

    }

};

// File:src/loaders/ObjectLoader.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.ObjectLoader = function ( manager ) {

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
    this.texturePath = '';

};

THREE.ObjectLoader.prototype = {

    constructor: THREE.ObjectLoader,

    load: function ( url, onLoad, onProgress, onError ) {

        if ( this.texturePath === '' ) {

            this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 );

        }

        var scope = this;

        var loader = new THREE.XHRLoader( scope.manager );
        loader.setCrossOrigin( this.crossOrigin );
        loader.load( url, function ( text ) {

            scope.parse( JSON.parse( text ), onLoad );

        }, onProgress, onError );

    },

    setTexturePath: function ( value ) {

        this.texturePath = value;

    },

    setCrossOrigin: function ( value ) {

        this.crossOrigin = value;

    },

    parse: function ( json, onLoad ) {

        var geometries = this.parseGeometries( json.geometries );

        var images = this.parseImages( json.images, function () {

            if ( onLoad !== undefined ) onLoad( object );

        } );

        var textures  = this.parseTextures( json.textures, images );
        var materials = this.parseMaterials( json.materials, textures );

        var object = this.parseObject( json.object, geometries, materials );

        if ( json.animations ) {

            object.animations = this.parseAnimations( json.animations );

        }

        if ( json.images === undefined || json.images.length === 0 ) {

            if ( onLoad !== undefined ) onLoad( object );

        }

        return object;

    },

    parseGeometries: function ( json ) {

        var geometries = {};

        if ( json !== undefined ) {

            var geometryLoader = new THREE.JSONLoader();
            var bufferGeometryLoader = new THREE.BufferGeometryLoader();

            for ( var i = 0, l = json.length; i < l; i ++ ) {

                var geometry;
                var data = json[ i ];

                switch ( data.type ) {

                    case 'PlaneGeometry':
                    case 'PlaneBufferGeometry':

                        geometry = new THREE[ data.type ](
                            data.width,
                            data.height,
                            data.widthSegments,
                            data.heightSegments
                        );

                        break;

                    case 'BoxGeometry':
                    case 'CubeGeometry': // backwards compatible

                        geometry = new THREE.BoxGeometry(
                            data.width,
                            data.height,
                            data.depth,
                            data.widthSegments,
                            data.heightSegments,
                            data.depthSegments
                        );

                        break;

                    case 'CircleBufferGeometry':

                        geometry = new THREE.CircleBufferGeometry(
                            data.radius,
                            data.segments,
                            data.thetaStart,
                            data.thetaLength
                        );

                        break;

                    case 'CircleGeometry':

                        geometry = new THREE.CircleGeometry(
                            data.radius,
                            data.segments,
                            data.thetaStart,
                            data.thetaLength
                        );

                        break;

                    case 'CylinderGeometry':

                        geometry = new THREE.CylinderGeometry(
                            data.radiusTop,
                            data.radiusBottom,
                            data.height,
                            data.radialSegments,
                            data.heightSegments,
                            data.openEnded,
                            data.thetaStart,
                            data.thetaLength
                        );

                        break;

                    case 'SphereGeometry':

                        geometry = new THREE.SphereGeometry(
                            data.radius,
                            data.widthSegments,
                            data.heightSegments,
                            data.phiStart,
                            data.phiLength,
                            data.thetaStart,
                            data.thetaLength
                        );

                        break;

                    case 'SphereBufferGeometry':

                        geometry = new THREE.SphereBufferGeometry(
                            data.radius,
                            data.widthSegments,
                            data.heightSegments,
                            data.phiStart,
                            data.phiLength,
                            data.thetaStart,
                            data.thetaLength
                        );

                        break;

                    case 'DodecahedronGeometry':

                        geometry = new THREE.DodecahedronGeometry(
                            data.radius,
                            data.detail
                        );

                        break;

                    case 'IcosahedronGeometry':

                        geometry = new THREE.IcosahedronGeometry(
                            data.radius,
                            data.detail
                        );

                        break;

                    case 'OctahedronGeometry':

                        geometry = new THREE.OctahedronGeometry(
                            data.radius,
                            data.detail
                        );

                        break;

                    case 'TetrahedronGeometry':

                        geometry = new THREE.TetrahedronGeometry(
                            data.radius,
                            data.detail
                        );

                        break;

                    case 'RingGeometry':

                        geometry = new THREE.RingGeometry(
                            data.innerRadius,
                            data.outerRadius,
                            data.thetaSegments,
                            data.phiSegments,
                            data.thetaStart,
                            data.thetaLength
                        );

                        break;

                    case 'TorusGeometry':

                        geometry = new THREE.TorusGeometry(
                            data.radius,
                            data.tube,
                            data.radialSegments,
                            data.tubularSegments,
                            data.arc
                        );

                        break;

                    case 'TorusKnotGeometry':

                        geometry = new THREE.TorusKnotGeometry(
                            data.radius,
                            data.tube,
                            data.radialSegments,
                            data.tubularSegments,
                            data.p,
                            data.q,
                            data.heightScale
                        );

                        break;

                    case 'BufferGeometry':

                        geometry = bufferGeometryLoader.parse( data );

                        break;

                    case 'Geometry':

                        geometry = geometryLoader.parse( data.data, this.texturePath ).geometry;

                        break;

                    default:

                        console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' );

                        continue;

                }

                geometry.uuid = data.uuid;

                if ( data.name !== undefined ) geometry.name = data.name;

                geometries[ data.uuid ] = geometry;

            }

        }

        return geometries;

    },

    parseMaterials: function ( json, textures ) {

        var materials = {};

        if ( json !== undefined ) {

            var loader = new THREE.MaterialLoader();
            loader.setTextures( textures );

            for ( var i = 0, l = json.length; i < l; i ++ ) {

                var material = loader.parse( json[ i ] );
                materials[ material.uuid ] = material;

            }

        }

        return materials;

    },

    parseAnimations: function ( json ) {

        var animations = [];

        for ( var i = 0; i < json.length; i ++ ) {

            var clip = THREE.AnimationClip.parse( json[i] );

            animations.push( clip );

        }

        return animations;

    },

    parseImages: function ( json, onLoad ) {

        var scope = this;
        var images = {};

        function loadImage( url ) {

            scope.manager.itemStart( url );

            return loader.load( url, function () {

                scope.manager.itemEnd( url );

            } );

        }

        if ( json !== undefined && json.length > 0 ) {

            var manager = new THREE.LoadingManager( onLoad );

            var loader = new THREE.ImageLoader( manager );
            loader.setCrossOrigin( this.crossOrigin );

            for ( var i = 0, l = json.length; i < l; i ++ ) {

                var image = json[ i ];
                var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url;

                images[ image.uuid ] = loadImage( path );

            }

        }

        return images;

    },

    parseTextures: function ( json, images ) {

        function parseConstant( value ) {

            if ( typeof( value ) === 'number' ) return value;

            console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value );

            return THREE[ value ];

        }

        var textures = {};

        if ( json !== undefined ) {

            for ( var i = 0, l = json.length; i < l; i ++ ) {

                var data = json[ i ];

                if ( data.image === undefined ) {

                    console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid );

                }

                if ( images[ data.image ] === undefined ) {

                    console.warn( 'THREE.ObjectLoader: Undefined image', data.image );

                }

                var texture = new THREE.Texture( images[ data.image ] );
                texture.needsUpdate = true;

                texture.uuid = data.uuid;

                if ( data.name !== undefined ) texture.name = data.name;
                if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping );
                if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] );
                if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] );
                if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter );
                if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter );
                if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy;
                if ( Array.isArray( data.wrap ) ) {

                    texture.wrapS = parseConstant( data.wrap[ 0 ] );
                    texture.wrapT = parseConstant( data.wrap[ 1 ] );

                }

                textures[ data.uuid ] = texture;

            }

        }

        return textures;

    },

    parseObject: function () {

        var matrix = new THREE.Matrix4();

        return function ( data, geometries, materials ) {

            var object;

            function getGeometry( name ) {

                if ( geometries[ name ] === undefined ) {

                    console.warn( 'THREE.ObjectLoader: Undefined geometry', name );

                }

                return geometries[ name ];

            }

            function getMaterial( name ) {

                if ( name === undefined ) return undefined;

                if ( materials[ name ] === undefined ) {

                    console.warn( 'THREE.ObjectLoader: Undefined material', name );

                }

                return materials[ name ];

            }

            switch ( data.type ) {

                case 'Scene':

                    object = new THREE.Scene();

                    break;

                case 'PerspectiveCamera':

                    object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far );

                    break;

                case 'OrthographicCamera':

                    object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far );

                    break;

                case 'AmbientLight':

                    object = new THREE.AmbientLight( data.color );

                    break;

                case 'DirectionalLight':

                    object = new THREE.DirectionalLight( data.color, data.intensity );

                    break;

                case 'PointLight':

                    object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay );

                    break;

                case 'SpotLight':

                    object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay );

                    break;

                case 'HemisphereLight':

                    object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity );

                    break;

                case 'Mesh':

                    object = new THREE.Mesh( getGeometry( data.geometry ), getMaterial( data.material ) );

                    break;

                case 'LOD':

                    object = new THREE.LOD();

                    break;

                case 'Line':

                    object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode );

                    break;

                case 'PointCloud':
                case 'Points':

                    object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) );

                    break;

                case 'Sprite':

                    object = new THREE.Sprite( getMaterial( data.material ) );

                    break;

                case 'Group':

                    object = new THREE.Group();

                    break;

                default:

                    object = new THREE.Object3D();

            }

            object.uuid = data.uuid;

            if ( data.name !== undefined ) object.name = data.name;
            if ( data.matrix !== undefined ) {

                matrix.fromArray( data.matrix );
                matrix.decompose( object.position, object.quaternion, object.scale );

            } else {

                if ( data.position !== undefined ) object.position.fromArray( data.position );
                if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation );
                if ( data.scale !== undefined ) object.scale.fromArray( data.scale );

            }

            if ( data.castShadow !== undefined ) object.castShadow = data.castShadow;
            if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow;

            if ( data.visible !== undefined ) object.visible = data.visible;
            if ( data.userData !== undefined ) object.userData = data.userData;

            if ( data.children !== undefined ) {

                for ( var child in data.children ) {

                    object.add( this.parseObject( data.children[ child ], geometries, materials ) );

                }

            }

            if ( data.type === 'LOD' ) {

                var levels = data.levels;

                for ( var l = 0; l < levels.length; l ++ ) {

                    var level = levels[ l ];
                    var child = object.getObjectByProperty( 'uuid', level.object );

                    if ( child !== undefined ) {

                        object.addLevel( child, level.distance );

                    }

                }

            }

            return object;

        }

    }()

};

// File:src/loaders/TextureLoader.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.TextureLoader = function ( manager ) {

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.TextureLoader.prototype = {

    constructor: THREE.TextureLoader,

    load: function ( url, onLoad, onProgress, onError ) {

        var texture = new THREE.Texture();

        var loader = new THREE.ImageLoader( this.manager );
        loader.setCrossOrigin( this.crossOrigin );
        loader.load( url, function ( image ) {

            texture.image = image;
            texture.needsUpdate = true;

            if ( onLoad !== undefined ) {

                onLoad( texture );

            }

        }, onProgress, onError );

        return texture;

    },

    setCrossOrigin: function ( value ) {

        this.crossOrigin = value;

    }

};

// File:src/loaders/CubeTextureLoader.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.CubeTextureLoader = function ( manager ) {

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.CubeTextureLoader.prototype = {

    constructor: THREE.CubeTextureLoader,

    load: function ( urls, onLoad, onProgress, onError ) {

        var texture = new THREE.CubeTexture( [] );

        var loader = new THREE.ImageLoader();
        loader.setCrossOrigin( this.crossOrigin );

        var loaded = 0;

        function loadTexture( i ) {

            loader.load( urls[ i ], function ( image ) {

                texture.images[ i ] = image;

                loaded ++;

                if ( loaded === 6 ) {

                    texture.needsUpdate = true;

                    if ( onLoad ) onLoad( texture );

                }

            }, undefined, onError );

        }

        for ( var i = 0; i < urls.length; ++ i ) {

            loadTexture( i );

        }

        return texture;

    },

    setCrossOrigin: function ( value ) {

        this.crossOrigin = value;

    }

};

// File:src/loaders/BinaryTextureLoader.js

/**
 * @author Nikos M. / https://github.com/foo123/
 *
 * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...)
 */

THREE.DataTextureLoader = THREE.BinaryTextureLoader = function ( manager ) {

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

    // override in sub classes
    this._parser = null;

};

THREE.BinaryTextureLoader.prototype = {

    constructor: THREE.BinaryTextureLoader,

    load: function ( url, onLoad, onProgress, onError ) {

        var scope = this;

        var texture = new THREE.DataTexture();

        var loader = new THREE.XHRLoader( this.manager );
        loader.setCrossOrigin( this.crossOrigin );
        loader.setResponseType( 'arraybuffer' );

        loader.load( url, function ( buffer ) {

            var texData = scope._parser( buffer );

            if ( ! texData ) return;

            if ( undefined !== texData.image ) {

                texture.image = texData.image;

            } else if ( undefined !== texData.data ) {

                texture.image.width = texData.width;
                texture.image.height = texData.height;
                texture.image.data = texData.data;

            }

            texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : THREE.ClampToEdgeWrapping;
            texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : THREE.ClampToEdgeWrapping;

            texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : THREE.LinearFilter;
            texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : THREE.LinearMipMapLinearFilter;

            texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1;

            if ( undefined !== texData.format ) {

                texture.format = texData.format;

            }
            if ( undefined !== texData.type ) {

                texture.type = texData.type;

            }

            if ( undefined !== texData.mipmaps ) {

                texture.mipmaps = texData.mipmaps;

            }

            if ( 1 === texData.mipmapCount ) {

                texture.minFilter = THREE.LinearFilter;

            }

            texture.needsUpdate = true;

            if ( onLoad ) onLoad( texture, texData );

        }, onProgress, onError );


        return texture;

    },

    setCrossOrigin: function ( value ) {

        this.crossOrigin = value;

    }

};

// File:src/loaders/CompressedTextureLoader.js

/**
 * @author mrdoob / http://mrdoob.com/
 *
 * Abstract Base class to block based textures loader (dds, pvr, ...)
 */

THREE.CompressedTextureLoader = function ( manager ) {

    this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

    // override in sub classes
    this._parser = null;

};


THREE.CompressedTextureLoader.prototype = {

    constructor: THREE.CompressedTextureLoader,

    load: function ( url, onLoad, onProgress, onError ) {

        var scope = this;

        var images = [];

        var texture = new THREE.CompressedTexture();
        texture.image = images;

        var loader = new THREE.XHRLoader( this.manager );
        loader.setCrossOrigin( this.crossOrigin );
        loader.setResponseType( 'arraybuffer' );

        if ( Array.isArray( url ) ) {

            var loaded = 0;

            var loadTexture = function ( i ) {

                loader.load( url[ i ], function ( buffer ) {

                    var texDatas = scope._parser( buffer, true );

                    images[ i ] = {
                        width: texDatas.width,
                        height: texDatas.height,
                        format: texDatas.format,
                        mipmaps: texDatas.mipmaps
                    };

                    loaded += 1;

                    if ( loaded === 6 ) {

                        if ( texDatas.mipmapCount === 1 )
                            texture.minFilter = THREE.LinearFilter;

                        texture.format = texDatas.format;
                        texture.needsUpdate = true;

                        if ( onLoad ) onLoad( texture );

                    }

                }, onProgress, onError );

            };

            for ( var i = 0, il = url.length; i < il; ++ i ) {

                loadTexture( i );

            }

        } else {

            // compressed cubemap texture stored in a single DDS file

            loader.load( url, function ( buffer ) {

                var texDatas = scope._parser( buffer, true );

                if ( texDatas.isCubemap ) {

                    var faces = texDatas.mipmaps.length / texDatas.mipmapCount;

                    for ( var f = 0; f < faces; f ++ ) {

                        images[ f ] = { mipmaps : [] };

                        for ( var i = 0; i < texDatas.mipmapCount; i ++ ) {

                            images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] );
                            images[ f ].format = texDatas.format;
                            images[ f ].width = texDatas.width;
                            images[ f ].height = texDatas.height;

                        }

                    }

                } else {

                    texture.image.width = texDatas.width;
                    texture.image.height = texDatas.height;
                    texture.mipmaps = texDatas.mipmaps;

                }

                if ( texDatas.mipmapCount === 1 ) {

                    texture.minFilter = THREE.LinearFilter;

                }

                texture.format = texDatas.format;
                texture.needsUpdate = true;

                if ( onLoad ) onLoad( texture );

            }, onProgress, onError );

        }

        return texture;

    },

    setCrossOrigin: function ( value ) {

        this.crossOrigin = value;

    }

};

// File:src/materials/Material.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Material = function () {

    Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } );

    this.uuid = THREE.Math.generateUUID();

    this.name = '';
    this.type = 'Material';

    this.side = THREE.FrontSide;

    this.opacity = 1;
    this.transparent = false;

    this.blending = THREE.NormalBlending;

    this.blendSrc = THREE.SrcAlphaFactor;
    this.blendDst = THREE.OneMinusSrcAlphaFactor;
    this.blendEquation = THREE.AddEquation;
    this.blendSrcAlpha = null;
    this.blendDstAlpha = null;
    this.blendEquationAlpha = null;

    this.depthFunc = THREE.LessEqualDepth;
    this.depthTest = true;
    this.depthWrite = true;

    this.colorWrite = true;

    this.precision = null; // override the renderer's default precision for this material

    this.polygonOffset = false;
    this.polygonOffsetFactor = 0;
    this.polygonOffsetUnits = 0;

    this.alphaTest = 0;

    this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer

    this.visible = true;

    this._needsUpdate = true;

};

THREE.Material.prototype = {

    constructor: THREE.Material,

    get needsUpdate () {

        return this._needsUpdate;

    },

    set needsUpdate ( value ) {

        if ( value === true ) this.update();

        this._needsUpdate = value;

    },

    setValues: function ( values ) {

        if ( values === undefined ) return;

        for ( var key in values ) {

            var newValue = values[ key ];

            if ( newValue === undefined ) {

                console.warn( "THREE.Material: '" + key + "' parameter is undefined." );
                continue;

            }

            var currentValue = this[ key ];

            if ( currentValue === undefined ) {

                console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." );
                continue;

            }

            if ( currentValue instanceof THREE.Color ) {

                currentValue.set( newValue );

            } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) {

                currentValue.copy( newValue );

            } else if ( key === 'overdraw' ) {

                // ensure overdraw is backwards-compatible with legacy boolean type
                this[ key ] = Number( newValue );

            } else {

                this[ key ] = newValue;

            }

        }

    },

    toJSON: function ( meta ) {

        var data = {
            metadata: {
                version: 4.4,
                type: 'Material',
                generator: 'Material.toJSON'
            }
        };

        // standard Material serialization
        data.uuid = this.uuid;
        data.type = this.type;
        if ( this.name !== '' ) data.name = this.name;

        if ( this.color instanceof THREE.Color ) data.color = this.color.getHex();
        if ( this.emissive instanceof THREE.Color ) data.emissive = this.emissive.getHex();
        if ( this.specular instanceof THREE.Color ) data.specular = this.specular.getHex();
        if ( this.shininess !== undefined ) data.shininess = this.shininess;

        if ( this.map instanceof THREE.Texture ) data.map = this.map.toJSON( meta ).uuid;
        if ( this.alphaMap instanceof THREE.Texture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;
        if ( this.lightMap instanceof THREE.Texture ) data.lightMap = this.lightMap.toJSON( meta ).uuid;
        if ( this.bumpMap instanceof THREE.Texture ) {

            data.bumpMap = this.bumpMap.toJSON( meta ).uuid;
            data.bumpScale = this.bumpScale;

        }
        if ( this.normalMap instanceof THREE.Texture ) {

            data.normalMap = this.normalMap.toJSON( meta ).uuid;
            data.normalScale = this.normalScale; // Removed for now, causes issue in editor ui.js

        }
        if ( this.displacementMap instanceof THREE.Texture ) {

            data.displacementMap = this.displacementMap.toJSON( meta ).uuid;
            data.displacementScale = this.displacementScale;
            data.displacementBias = this.displacementBias;

        }
        if ( this.specularMap instanceof THREE.Texture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;
        if ( this.envMap instanceof THREE.Texture ) {

            data.envMap = this.envMap.toJSON( meta ).uuid;
            data.reflectivity = this.reflectivity; // Scale behind envMap

        }

        if ( this.size !== undefined ) data.size = this.size;
        if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;

        if ( this.vertexColors !== undefined && this.vertexColors !== THREE.NoColors ) data.vertexColors = this.vertexColors;
        if ( this.shading !== undefined && this.shading !== THREE.SmoothShading ) data.shading = this.shading;
        if ( this.blending !== undefined && this.blending !== THREE.NormalBlending ) data.blending = this.blending;
        if ( this.side !== undefined && this.side !== THREE.FrontSide ) data.side = this.side;

        if ( this.opacity < 1 ) data.opacity = this.opacity;
        if ( this.transparent === true ) data.transparent = this.transparent;
        if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
        if ( this.wireframe === true ) data.wireframe = this.wireframe;
        if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;

        return data;

    },

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( source ) {

        this.name = source.name;

        this.side = source.side;

        this.opacity = source.opacity;
        this.transparent = source.transparent;

        this.blending = source.blending;

        this.blendSrc = source.blendSrc;
        this.blendDst = source.blendDst;
        this.blendEquation = source.blendEquation;
        this.blendSrcAlpha = source.blendSrcAlpha;
        this.blendDstAlpha = source.blendDstAlpha;
        this.blendEquationAlpha = source.blendEquationAlpha;

        this.depthFunc = source.depthFunc;
        this.depthTest = source.depthTest;
        this.depthWrite = source.depthWrite;

        this.precision = source.precision;

        this.polygonOffset = source.polygonOffset;
        this.polygonOffsetFactor = source.polygonOffsetFactor;
        this.polygonOffsetUnits = source.polygonOffsetUnits;

        this.alphaTest = source.alphaTest;

        this.overdraw = source.overdraw;

        this.visible = source.visible;

        return this;

    },

    update: function () {

        this.dispatchEvent( { type: 'update' } );

    },

    dispose: function () {

        this.dispatchEvent( { type: 'dispose' } );

    },

    // Deprecated

    get wrapAround () {

        console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' );

    },

    set wrapAround ( boolean ) {

        console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' );

    },

    get wrapRGB () {

        console.warn( 'THREE.' + this.type + ': .wrapRGB has been removed.' );
        return new THREE.Color();

    }

};

THREE.EventDispatcher.prototype.apply( THREE.Material.prototype );

THREE.MaterialIdCount = 0;

// File:src/materials/LineBasicMaterial.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 *
 * parameters = {
 *  color: <hex>,
 *  opacity: <float>,
 *
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  linewidth: <float>,
 *  linecap: "round",
 *  linejoin: "round",
 *
 *  vertexColors: <bool>
 *
 *  fog: <bool>
 * }
 */

THREE.LineBasicMaterial = function ( parameters ) {

    THREE.Material.call( this );

    this.type = 'LineBasicMaterial';

    this.color = new THREE.Color( 0xffffff );

    this.linewidth = 1;
    this.linecap = 'round';
    this.linejoin = 'round';

    this.vertexColors = THREE.NoColors;

    this.fog = true;

    this.setValues( parameters );

};

THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial;

THREE.LineBasicMaterial.prototype.copy = function ( source ) {

    THREE.Material.prototype.copy.call( this, source );

    this.color.copy( source.color );

    this.linewidth = source.linewidth;
    this.linecap = source.linecap;
    this.linejoin = source.linejoin;

    this.vertexColors = source.vertexColors;

    this.fog = source.fog;

    return this;

};

// File:src/materials/LineDashedMaterial.js

/**
 * @author alteredq / http://alteredqualia.com/
 *
 * parameters = {
 *  color: <hex>,
 *  opacity: <float>,
 *
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  linewidth: <float>,
 *
 *  scale: <float>,
 *  dashSize: <float>,
 *  gapSize: <float>,
 *
 *  vertexColors: <bool>
 *
 *  fog: <bool>
 * }
 */

THREE.LineDashedMaterial = function ( parameters ) {

    THREE.Material.call( this );

    this.type = 'LineDashedMaterial';

    this.color = new THREE.Color( 0xffffff );

    this.linewidth = 1;

    this.scale = 1;
    this.dashSize = 3;
    this.gapSize = 1;

    this.vertexColors = false;

    this.fog = true;

    this.setValues( parameters );

};

THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial;

THREE.LineDashedMaterial.prototype.copy = function ( source ) {

    THREE.Material.prototype.copy.call( this, source );

    this.color.copy( source.color );

    this.linewidth = source.linewidth;

    this.scale = source.scale;
    this.dashSize = source.dashSize;
    this.gapSize = source.gapSize;

    this.vertexColors = source.vertexColors;

    this.fog = source.fog;

    return this;

};

// File:src/materials/MeshBasicMaterial.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 *
 * parameters = {
 *  color: <hex>,
 *  opacity: <float>,
 *  map: new THREE.Texture( <Image> ),
 *
 *  aoMap: new THREE.Texture( <Image> ),
 *  aoMapIntensity: <float>
 *
 *  specularMap: new THREE.Texture( <Image> ),
 *
 *  alphaMap: new THREE.Texture( <Image> ),
 *
 *  envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
 *  combine: THREE.Multiply,
 *  reflectivity: <float>,
 *  refractionRatio: <float>,
 *
 *  shading: THREE.SmoothShading,
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  wireframe: <boolean>,
 *  wireframeLinewidth: <float>,
 *
 *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
 *
 *  skinning: <bool>,
 *  morphTargets: <bool>,
 *
 *  fog: <bool>
 * }
 */

THREE.MeshBasicMaterial = function ( parameters ) {

    THREE.Material.call( this );

    this.type = 'MeshBasicMaterial';

    this.color = new THREE.Color( 0xffffff ); // emissive

    this.map = null;

    this.aoMap = null;
    this.aoMapIntensity = 1.0;

    this.specularMap = null;

    this.alphaMap = null;

    this.envMap = null;
    this.combine = THREE.MultiplyOperation;
    this.reflectivity = 1;
    this.refractionRatio = 0.98;

    this.fog = true;

    this.shading = THREE.SmoothShading;

    this.wireframe = false;
    this.wireframeLinewidth = 1;
    this.wireframeLinecap = 'round';
    this.wireframeLinejoin = 'round';

    this.vertexColors = THREE.NoColors;

    this.skinning = false;
    this.morphTargets = false;

    this.setValues( parameters );

};

THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial;

THREE.MeshBasicMaterial.prototype.copy = function ( source ) {

    THREE.Material.prototype.copy.call( this, source );

    this.color.copy( source.color );

    this.map = source.map;

    this.aoMap = source.aoMap;
    this.aoMapIntensity = source.aoMapIntensity;

    this.specularMap = source.specularMap;

    this.alphaMap = source.alphaMap;

    this.envMap = source.envMap;
    this.combine = source.combine;
    this.reflectivity = source.reflectivity;
    this.refractionRatio = source.refractionRatio;

    this.fog = source.fog;

    this.shading = source.shading;

    this.wireframe = source.wireframe;
    this.wireframeLinewidth = source.wireframeLinewidth;
    this.wireframeLinecap = source.wireframeLinecap;
    this.wireframeLinejoin = source.wireframeLinejoin;

    this.vertexColors = source.vertexColors;

    this.skinning = source.skinning;
    this.morphTargets = source.morphTargets;

    return this;

};

// File:src/materials/MeshLambertMaterial.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 *
 * parameters = {
 *  color: <hex>,
 *  emissive: <hex>,
 *  opacity: <float>,
 *
 *  map: new THREE.Texture( <Image> ),
 *
 *  specularMap: new THREE.Texture( <Image> ),
 *
 *  alphaMap: new THREE.Texture( <Image> ),
 *
 *  envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
 *  combine: THREE.Multiply,
 *  reflectivity: <float>,
 *  refractionRatio: <float>,
 *
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  wireframe: <boolean>,
 *  wireframeLinewidth: <float>,
 *
 *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
 *
 *  skinning: <bool>,
 *  morphTargets: <bool>,
 *  morphNormals: <bool>,
 *
 *	fog: <bool>
 * }
 */

THREE.MeshLambertMaterial = function ( parameters ) {

    THREE.Material.call( this );

    this.type = 'MeshLambertMaterial';

    this.color = new THREE.Color( 0xffffff ); // diffuse
    this.emissive = new THREE.Color( 0x000000 );

    this.map = null;

    this.specularMap = null;

    this.alphaMap = null;

    this.envMap = null;
    this.combine = THREE.MultiplyOperation;
    this.reflectivity = 1;
    this.refractionRatio = 0.98;

    this.fog = true;

    this.wireframe = false;
    this.wireframeLinewidth = 1;
    this.wireframeLinecap = 'round';
    this.wireframeLinejoin = 'round';

    this.vertexColors = THREE.NoColors;

    this.skinning = false;
    this.morphTargets = false;
    this.morphNormals = false;

    this.setValues( parameters );

};

THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial;

THREE.MeshLambertMaterial.prototype.copy = function ( source ) {

    THREE.Material.prototype.copy.call( this, source );

    this.color.copy( source.color );
    this.emissive.copy( source.emissive );

    this.map = source.map;

    this.specularMap = source.specularMap;

    this.alphaMap = source.alphaMap;

    this.envMap = source.envMap;
    this.combine = source.combine;
    this.reflectivity = source.reflectivity;
    this.refractionRatio = source.refractionRatio;

    this.fog = source.fog;

    this.wireframe = source.wireframe;
    this.wireframeLinewidth = source.wireframeLinewidth;
    this.wireframeLinecap = source.wireframeLinecap;
    this.wireframeLinejoin = source.wireframeLinejoin;

    this.vertexColors = source.vertexColors;

    this.skinning = source.skinning;
    this.morphTargets = source.morphTargets;
    this.morphNormals = source.morphNormals;

    return this;

};

// File:src/materials/MeshPhongMaterial.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 *
 * parameters = {
 *  color: <hex>,
 *  emissive: <hex>,
 *  specular: <hex>,
 *  shininess: <float>,
 *  opacity: <float>,
 *
 *  map: new THREE.Texture( <Image> ),
 *
 *  lightMap: new THREE.Texture( <Image> ),
 *  lightMapIntensity: <float>
 *
 *  aoMap: new THREE.Texture( <Image> ),
 *  aoMapIntensity: <float>
 *
 *  emissiveMap: new THREE.Texture( <Image> ),
 *
 *  bumpMap: new THREE.Texture( <Image> ),
 *  bumpScale: <float>,
 *
 *  normalMap: new THREE.Texture( <Image> ),
 *  normalScale: <Vector2>,
 *
 *  displacementMap: new THREE.Texture( <Image> ),
 *  displacementScale: <float>,
 *  displacementBias: <float>,
 *
 *  specularMap: new THREE.Texture( <Image> ),
 *
 *  alphaMap: new THREE.Texture( <Image> ),
 *
 *  envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ),
 *  combine: THREE.Multiply,
 *  reflectivity: <float>,
 *  refractionRatio: <float>,
 *
 *  shading: THREE.SmoothShading,
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  wireframe: <boolean>,
 *  wireframeLinewidth: <float>,
 *
 *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
 *
 *  skinning: <bool>,
 *  morphTargets: <bool>,
 *  morphNormals: <bool>,
 *
 *	fog: <bool>
 * }
 */

THREE.MeshPhongMaterial = function ( parameters ) {

    THREE.Material.call( this );

    this.type = 'MeshPhongMaterial';

    this.color = new THREE.Color( 0xffffff ); // diffuse
    this.emissive = new THREE.Color( 0x000000 );
    this.specular = new THREE.Color( 0x111111 );
    this.shininess = 30;

    this.metal = false;

    this.map = null;

    this.lightMap = null;
    this.lightMapIntensity = 1.0;

    this.aoMap = null;
    this.aoMapIntensity = 1.0;

    this.emissiveMap = null;

    this.bumpMap = null;
    this.bumpScale = 1;

    this.normalMap = null;
    this.normalScale = new THREE.Vector2( 1, 1 );

    this.displacementMap = null;
    this.displacementScale = 1;
    this.displacementBias = 0;

    this.specularMap = null;

    this.alphaMap = null;

    this.envMap = null;
    this.combine = THREE.MultiplyOperation;
    this.reflectivity = 1;
    this.refractionRatio = 0.98;

    this.fog = true;

    this.shading = THREE.SmoothShading;

    this.wireframe = false;
    this.wireframeLinewidth = 1;
    this.wireframeLinecap = 'round';
    this.wireframeLinejoin = 'round';

    this.vertexColors = THREE.NoColors;

    this.skinning = false;
    this.morphTargets = false;
    this.morphNormals = false;

    this.setValues( parameters );

};

THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial;

THREE.MeshPhongMaterial.prototype.copy = function ( source ) {

    THREE.Material.prototype.copy.call( this, source );

    this.color.copy( source.color );
    this.emissive.copy( source.emissive );
    this.specular.copy( source.specular );
    this.shininess = source.shininess;

    this.metal = source.metal;

    this.map = source.map;

    this.lightMap = source.lightMap;
    this.lightMapIntensity = source.lightMapIntensity;

    this.aoMap = source.aoMap;
    this.aoMapIntensity = source.aoMapIntensity;

    this.emissiveMap = source.emissiveMap;

    this.bumpMap = source.bumpMap;
    this.bumpScale = source.bumpScale;

    this.normalMap = source.normalMap;
    this.normalScale.copy( source.normalScale );

    this.displacementMap = source.displacementMap;
    this.displacementScale = source.displacementScale;
    this.displacementBias = source.displacementBias;

    this.specularMap = source.specularMap;

    this.alphaMap = source.alphaMap;

    this.envMap = source.envMap;
    this.combine = source.combine;
    this.reflectivity = source.reflectivity;
    this.refractionRatio = source.refractionRatio;

    this.fog = source.fog;

    this.shading = source.shading;

    this.wireframe = source.wireframe;
    this.wireframeLinewidth = source.wireframeLinewidth;
    this.wireframeLinecap = source.wireframeLinecap;
    this.wireframeLinejoin = source.wireframeLinejoin;

    this.vertexColors = source.vertexColors;

    this.skinning = source.skinning;
    this.morphTargets = source.morphTargets;
    this.morphNormals = source.morphNormals;

    return this;

};

// File:src/materials/MeshDepthMaterial.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 *
 * parameters = {
 *  opacity: <float>,
 *
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  wireframe: <boolean>,
 *  wireframeLinewidth: <float>
 * }
 */

THREE.MeshDepthMaterial = function ( parameters ) {

    THREE.Material.call( this );

    this.type = 'MeshDepthMaterial';

    this.morphTargets = false;
    this.wireframe = false;
    this.wireframeLinewidth = 1;

    this.setValues( parameters );

};

THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial;

THREE.MeshDepthMaterial.prototype.copy = function ( source ) {

    THREE.Material.prototype.copy.call( this, source );

    this.wireframe = source.wireframe;
    this.wireframeLinewidth = source.wireframeLinewidth;

    return this;

};

// File:src/materials/MeshNormalMaterial.js

/**
 * @author mrdoob / http://mrdoob.com/
 *
 * parameters = {
 *  opacity: <float>,
 *
 *  shading: THREE.FlatShading,
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  wireframe: <boolean>,
 *  wireframeLinewidth: <float>
 * }
 */

THREE.MeshNormalMaterial = function ( parameters ) {

    THREE.Material.call( this, parameters );

    this.type = 'MeshNormalMaterial';

    this.wireframe = false;
    this.wireframeLinewidth = 1;

    this.morphTargets = false;

    this.setValues( parameters );

};

THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial;

THREE.MeshNormalMaterial.prototype.copy = function ( source ) {

    THREE.Material.prototype.copy.call( this, source );

    this.wireframe = source.wireframe;
    this.wireframeLinewidth = source.wireframeLinewidth;

    return this;

};

// File:src/materials/MultiMaterial.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.MultiMaterial = function ( materials ) {

    this.uuid = THREE.Math.generateUUID();

    this.type = 'MultiMaterial';

    this.materials = materials instanceof Array ? materials : [];

    this.visible = true;

};

THREE.MultiMaterial.prototype = {

    constructor: THREE.MultiMaterial,

    toJSON: function () {

        var output = {
            metadata: {
                version: 4.2,
                type: 'material',
                generator: 'MaterialExporter'
            },
            uuid: this.uuid,
            type: this.type,
            materials: []
        };

        for ( var i = 0, l = this.materials.length; i < l; i ++ ) {

            output.materials.push( this.materials[ i ].toJSON() );

        }

        output.visible = this.visible;

        return output;

    },

    clone: function () {

        var material = new this.constructor();

        for ( var i = 0; i < this.materials.length; i ++ ) {

            material.materials.push( this.materials[ i ].clone() );

        }

        material.visible = this.visible;

        return material;

    }

};

// backwards compatibility

THREE.MeshFaceMaterial = THREE.MultiMaterial;

// File:src/materials/PointsMaterial.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 *
 * parameters = {
 *  color: <hex>,
 *  opacity: <float>,
 *  map: new THREE.Texture( <Image> ),
 *
 *  size: <float>,
 *  sizeAttenuation: <bool>,
 *
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  vertexColors: <bool>,
 *
 *  fog: <bool>
 * }
 */

THREE.PointsMaterial = function ( parameters ) {

    THREE.Material.call( this );

    this.type = 'PointsMaterial';

    this.color = new THREE.Color( 0xffffff );

    this.map = null;

    this.size = 1;
    this.sizeAttenuation = true;

    this.vertexColors = THREE.NoColors;

    this.fog = true;

    this.setValues( parameters );

};

THREE.PointsMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.PointsMaterial.prototype.constructor = THREE.PointsMaterial;

THREE.PointsMaterial.prototype.copy = function ( source ) {

    THREE.Material.prototype.copy.call( this, source );

    this.color.copy( source.color );

    this.map = source.map;

    this.size = source.size;
    this.sizeAttenuation = source.sizeAttenuation;

    this.vertexColors = source.vertexColors;

    this.fog = source.fog;

    return this;

};

// backwards compatibility

THREE.PointCloudMaterial = function ( parameters ) {

    console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' );
    return new THREE.PointsMaterial( parameters );

};

THREE.ParticleBasicMaterial = function ( parameters ) {

    console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' );
    return new THREE.PointsMaterial( parameters );

};

THREE.ParticleSystemMaterial = function ( parameters ) {

    console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' );
    return new THREE.PointsMaterial( parameters );

};

// File:src/materials/ShaderMaterial.js

/**
 * @author alteredq / http://alteredqualia.com/
 *
 * parameters = {
 *  defines: { "label" : "value" },
 *  uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } },
 *
 *  fragmentShader: <string>,
 *  vertexShader: <string>,
 *
 *  shading: THREE.SmoothShading,
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *  wireframe: <boolean>,
 *  wireframeLinewidth: <float>,
 *
 *  lights: <bool>,
 *
 *  vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors,
 *
 *  skinning: <bool>,
 *  morphTargets: <bool>,
 *  morphNormals: <bool>,
 *
 *	fog: <bool>
 * }
 */

THREE.ShaderMaterial = function ( parameters ) {

    THREE.Material.call( this );

    this.type = 'ShaderMaterial';

    this.defines = {};
    this.uniforms = {};

    this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}';
    this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}';

    this.shading = THREE.SmoothShading;

    this.linewidth = 1;

    this.wireframe = false;
    this.wireframeLinewidth = 1;

    this.fog = false; // set to use scene fog

    this.lights = false; // set to use scene lights

    this.vertexColors = THREE.NoColors; // set to use "color" attribute stream

    this.skinning = false; // set to use skinning attribute streams

    this.morphTargets = false; // set to use morph targets
    this.morphNormals = false; // set to use morph normals

    this.derivatives = false; // set to use derivatives

    // When rendered geometry doesn't include these attributes but the material does,
    // use these default values in WebGL. This avoids errors when buffer data is missing.
    this.defaultAttributeValues = {
        'color': [ 1, 1, 1 ],
        'uv': [ 0, 0 ],
        'uv2': [ 0, 0 ]
    };

    this.index0AttributeName = undefined;

    if ( parameters !== undefined ) {

        if ( parameters.attributes !== undefined ) {

            console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' );

        }

        this.setValues( parameters );

    }

};

THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial;

THREE.ShaderMaterial.prototype.copy = function ( source ) {

    THREE.Material.prototype.copy.call( this, source );

    this.fragmentShader = source.fragmentShader;
    this.vertexShader = source.vertexShader;

    this.uniforms = THREE.UniformsUtils.clone( source.uniforms );

    this.attributes = source.attributes;
    this.defines = source.defines;

    this.shading = source.shading;

    this.wireframe = source.wireframe;
    this.wireframeLinewidth = source.wireframeLinewidth;

    this.fog = source.fog;

    this.lights = source.lights;

    this.vertexColors = source.vertexColors;

    this.skinning = source.skinning;

    this.morphTargets = source.morphTargets;
    this.morphNormals = source.morphNormals;

    this.derivatives = source.derivatives;

    return this;

};

THREE.ShaderMaterial.prototype.toJSON = function ( meta ) {

    var data = THREE.Material.prototype.toJSON.call( this, meta );

    data.uniforms = this.uniforms;
    data.attributes = this.attributes;
    data.vertexShader = this.vertexShader;
    data.fragmentShader = this.fragmentShader;

    return data;

};

// File:src/materials/RawShaderMaterial.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.RawShaderMaterial = function ( parameters ) {

    THREE.ShaderMaterial.call( this, parameters );

    this.type = 'RawShaderMaterial';

};

THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial;
// File:src/materials/SpriteMaterial.js

/**
 * @author alteredq / http://alteredqualia.com/
 *
 * parameters = {
 *  color: <hex>,
 *  opacity: <float>,
 *  map: new THREE.Texture( <Image> ),
 *
 *  blending: THREE.NormalBlending,
 *  depthTest: <bool>,
 *  depthWrite: <bool>,
 *
 *	uvOffset: new THREE.Vector2(),
 *	uvScale: new THREE.Vector2(),
 *
 *  fog: <bool>
 * }
 */

THREE.SpriteMaterial = function ( parameters ) {

    THREE.Material.call( this );

    this.type = 'SpriteMaterial';

    this.color = new THREE.Color( 0xffffff );
    this.map = null;

    this.rotation = 0;

    this.fog = false;

    // set parameters

    this.setValues( parameters );

};

THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial;

THREE.SpriteMaterial.prototype.copy = function ( source ) {

    THREE.Material.prototype.copy.call( this, source );

    this.color.copy( source.color );
    this.map = source.map;

    this.rotation = source.rotation;

    this.fog = source.fog;

    return this;

};

// File:src/textures/Texture.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 * @author szimek / https://github.com/szimek/
 */

THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {

    Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } );

    this.uuid = THREE.Math.generateUUID();

    this.name = '';
    this.sourceFile = '';

    this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE;
    this.mipmaps = [];

    this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING;

    this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping;
    this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping;

    this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter;
    this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter;

    this.anisotropy = anisotropy !== undefined ? anisotropy : 1;

    this.format = format !== undefined ? format : THREE.RGBAFormat;
    this.type = type !== undefined ? type : THREE.UnsignedByteType;

    this.offset = new THREE.Vector2( 0, 0 );
    this.repeat = new THREE.Vector2( 1, 1 );

    this.generateMipmaps = true;
    this.premultiplyAlpha = false;
    this.flipY = true;
    this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)

    this.version = 0;
    this.onUpdate = null;

};

THREE.Texture.DEFAULT_IMAGE = undefined;
THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping;

THREE.Texture.prototype = {

    constructor: THREE.Texture,

    set needsUpdate ( value ) {

        if ( value === true ) this.version ++;

    },

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( source ) {

        this.image = source.image;
        this.mipmaps = source.mipmaps.slice( 0 );

        this.mapping = source.mapping;

        this.wrapS = source.wrapS;
        this.wrapT = source.wrapT;

        this.magFilter = source.magFilter;
        this.minFilter = source.minFilter;

        this.anisotropy = source.anisotropy;

        this.format = source.format;
        this.type = source.type;

        this.offset.copy( source.offset );
        this.repeat.copy( source.repeat );

        this.generateMipmaps = source.generateMipmaps;
        this.premultiplyAlpha = source.premultiplyAlpha;
        this.flipY = source.flipY;
        this.unpackAlignment = source.unpackAlignment;

        return this;

    },

    toJSON: function ( meta ) {

        if ( meta.textures[ this.uuid ] !== undefined ) {

            return meta.textures[ this.uuid ];

        }

        function getDataURL( image ) {

            var canvas;

            if ( image.toDataURL !== undefined ) {

                canvas = image;

            } else {

                canvas = document.createElement( 'canvas' );
                canvas.width = image.width;
                canvas.height = image.height;

                canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height );

            }

            if ( canvas.width > 2048 || canvas.height > 2048 ) {

                return canvas.toDataURL( 'image/jpeg', 0.6 );

            } else {

                return canvas.toDataURL( 'image/png' );

            }

        }

        var output = {
            metadata: {
                version: 4.4,
                type: 'Texture',
                generator: 'Texture.toJSON'
            },

            uuid: this.uuid,
            name: this.name,

            mapping: this.mapping,

            repeat: [ this.repeat.x, this.repeat.y ],
            offset: [ this.offset.x, this.offset.y ],
            wrap: [ this.wrapS, this.wrapT ],

            minFilter: this.minFilter,
            magFilter: this.magFilter,
            anisotropy: this.anisotropy
        };

        if ( this.image !== undefined ) {

            // TODO: Move to THREE.Image

            var image = this.image;

            if ( image.uuid === undefined ) {

                image.uuid = THREE.Math.generateUUID(); // UGH

            }

            if ( meta.images[ image.uuid ] === undefined ) {

                meta.images[ image.uuid ] = {
                    uuid: image.uuid,
                    url: getDataURL( image )
                };

            }

            output.image = image.uuid;

        }

        meta.textures[ this.uuid ] = output;

        return output;

    },

    dispose: function () {

        this.dispatchEvent( { type: 'dispose' } );

    },

    transformUv: function ( uv ) {

        if ( this.mapping !== THREE.UVMapping )  return;

        uv.multiply( this.repeat );
        uv.add( this.offset );

        if ( uv.x < 0 || uv.x > 1 ) {

            switch ( this.wrapS ) {

                case THREE.RepeatWrapping:

                    uv.x = uv.x - Math.floor( uv.x );
                    break;

                case THREE.ClampToEdgeWrapping:

                    uv.x = uv.x < 0 ? 0 : 1;
                    break;

                case THREE.MirroredRepeatWrapping:

                    if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {

                        uv.x = Math.ceil( uv.x ) - uv.x;

                    } else {

                        uv.x = uv.x - Math.floor( uv.x );

                    }
                    break;

            }

        }

        if ( uv.y < 0 || uv.y > 1 ) {

            switch ( this.wrapT ) {

                case THREE.RepeatWrapping:

                    uv.y = uv.y - Math.floor( uv.y );
                    break;

                case THREE.ClampToEdgeWrapping:

                    uv.y = uv.y < 0 ? 0 : 1;
                    break;

                case THREE.MirroredRepeatWrapping:

                    if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {

                        uv.y = Math.ceil( uv.y ) - uv.y;

                    } else {

                        uv.y = uv.y - Math.floor( uv.y );

                    }
                    break;

            }

        }

        if ( this.flipY ) {

            uv.y = 1 - uv.y;

        }

    }

};

THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype );

THREE.TextureIdCount = 0;

// File:src/textures/CanvasTexture.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.CanvasTexture = function ( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {

    THREE.Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );

    this.needsUpdate = true;

};

THREE.CanvasTexture.prototype = Object.create( THREE.Texture.prototype );
THREE.CanvasTexture.prototype.constructor = THREE.CanvasTexture;

// File:src/textures/CubeTexture.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {

    mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping;

    THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );

    this.images = images;
    this.flipY = false;

};

THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype );
THREE.CubeTexture.prototype.constructor = THREE.CubeTexture;

THREE.CubeTexture.prototype.copy = function ( source ) {

    THREE.Texture.prototype.copy.call( this, source );

    this.images = source.images;

    return this;

};
// File:src/textures/CompressedTexture.js

/**
 * @author alteredq / http://alteredqualia.com/
 */

THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {

    THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );

    this.image = { width: width, height: height };
    this.mipmaps = mipmaps;

    // no flipping for cube textures
    // (also flipping doesn't work for compressed textures )

    this.flipY = false;

    // can't generate mipmaps for compressed textures
    // mips must be embedded in DDS files

    this.generateMipmaps = false;

};

THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype );
THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture;

// File:src/textures/DataTexture.js

/**
 * @author alteredq / http://alteredqualia.com/
 */

THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) {

    THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );

    this.image = { data: data, width: width, height: height };

    this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter;
    this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter;

    this.flipY = false;
    this.generateMipmaps  = false;

};

THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype );
THREE.DataTexture.prototype.constructor = THREE.DataTexture;

// File:src/textures/VideoTexture.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {

    THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );

    this.generateMipmaps = false;

    var scope = this;

    function update() {

        requestAnimationFrame( update );

        if ( video.readyState === video.HAVE_ENOUGH_DATA ) {

            scope.needsUpdate = true;

        }

    }

    update();

};

THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype );
THREE.VideoTexture.prototype.constructor = THREE.VideoTexture;

// File:src/objects/Group.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.Group = function () {

    THREE.Object3D.call( this );

    this.type = 'Group';

};

THREE.Group.prototype = Object.create( THREE.Object3D.prototype );
THREE.Group.prototype.constructor = THREE.Group;
// File:src/objects/Points.js

/**
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Points = function ( geometry, material ) {

    THREE.Object3D.call( this );

    this.type = 'Points';

    this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
    this.material = material !== undefined ? material : new THREE.PointsMaterial( { color: Math.random() * 0xffffff } );

};

THREE.Points.prototype = Object.create( THREE.Object3D.prototype );
THREE.Points.prototype.constructor = THREE.Points;

THREE.Points.prototype.raycast = ( function () {

    var inverseMatrix = new THREE.Matrix4();
    var ray = new THREE.Ray();

    return function raycast( raycaster, intersects ) {

        var object = this;
        var geometry = object.geometry;
        var threshold = raycaster.params.Points.threshold;

        inverseMatrix.getInverse( this.matrixWorld );
        ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );

        if ( geometry.boundingBox !== null ) {

            if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) {

                return;

            }

        }

        var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
        var localThresholdSq = localThreshold * localThreshold;
        var position = new THREE.Vector3();

        function testPoint( point, index ) {

            var rayPointDistanceSq = ray.distanceSqToPoint( point );

            if ( rayPointDistanceSq < localThresholdSq ) {

                var intersectPoint = ray.closestPointToPoint( point );
                intersectPoint.applyMatrix4( object.matrixWorld );

                var distance = raycaster.ray.origin.distanceTo( intersectPoint );

                if ( distance < raycaster.near || distance > raycaster.far ) return;

                intersects.push( {

                    distance: distance,
                    distanceToRay: Math.sqrt( rayPointDistanceSq ),
                    point: intersectPoint.clone(),
                    index: index,
                    face: null,
                    object: object

                } );

            }

        }

        if ( geometry instanceof THREE.BufferGeometry ) {

            var index = geometry.index;
            var attributes = geometry.attributes;
            var positions = attributes.position.array;

            if ( index !== null ) {

                var indices = index.array;

                for ( var i = 0, il = indices.length; i < il; i ++ ) {

                    var a = indices[ i ];

                    position.fromArray( positions, a * 3 );

                    testPoint( position, a );

                }

            } else {

                for ( var i = 0, l = positions.length / 3; i < l; i ++ ) {

                    position.fromArray( positions, i * 3 );

                    testPoint( position, i );

                }

            }

        } else {

            var vertices = geometry.vertices;

            for ( var i = 0, l = vertices.length; i < l; i ++ ) {

                testPoint( vertices[ i ], i );

            }

        }

    };

}() );

THREE.Points.prototype.clone = function () {

    return new this.constructor( this.geometry, this.material ).copy( this );

};

// Backwards compatibility

THREE.PointCloud = function ( geometry, material ) {

    console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' );
    return new THREE.Points( geometry, material );

};

THREE.ParticleSystem = function ( geometry, material ) {

    console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' );
    return new THREE.Points( geometry, material );

};

// File:src/objects/Line.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.Line = function ( geometry, material, mode ) {

    if ( mode === 1 ) {

        console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' );
        return new THREE.LineSegments( geometry, material );

    }

    THREE.Object3D.call( this );

    this.type = 'Line';

    this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
    this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } );

};

THREE.Line.prototype = Object.create( THREE.Object3D.prototype );
THREE.Line.prototype.constructor = THREE.Line;

THREE.Line.prototype.raycast = ( function () {

    var inverseMatrix = new THREE.Matrix4();
    var ray = new THREE.Ray();
    var sphere = new THREE.Sphere();

    return function raycast( raycaster, intersects ) {

        var precision = raycaster.linePrecision;
        var precisionSq = precision * precision;

        var geometry = this.geometry;

        if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();

        // Checking boundingSphere distance to ray

        sphere.copy( geometry.boundingSphere );
        sphere.applyMatrix4( this.matrixWorld );

        if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {

            return;

        }

        inverseMatrix.getInverse( this.matrixWorld );
        ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );

        var vStart = new THREE.Vector3();
        var vEnd = new THREE.Vector3();
        var interSegment = new THREE.Vector3();
        var interRay = new THREE.Vector3();
        var step = this instanceof THREE.LineSegments ? 2 : 1;

        if ( geometry instanceof THREE.BufferGeometry ) {

            var index = geometry.index;
            var attributes = geometry.attributes;

            if ( index !== null ) {

                var indices = index.array;
                var positions = attributes.position.array;

                for ( var i = 0, l = indices.length - 1; i < l; i += step ) {

                    var a = indices[ i ];
                    var b = indices[ i + 1 ];

                    vStart.fromArray( positions, a * 3 );
                    vEnd.fromArray( positions, b * 3 );

                    var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );

                    if ( distSq > precisionSq ) continue;

                    interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation

                    var distance = raycaster.ray.origin.distanceTo( interRay );

                    if ( distance < raycaster.near || distance > raycaster.far ) continue;

                    intersects.push( {

                        distance: distance,
                        // What do we want? intersection point on the ray or on the segment??
                        // point: raycaster.ray.at( distance ),
                        point: interSegment.clone().applyMatrix4( this.matrixWorld ),
                        index: i,
                        face: null,
                        faceIndex: null,
                        object: this

                    } );

                }

            } else {

                var positions = attributes.position.array;

                for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) {

                    vStart.fromArray( positions, 3 * i );
                    vEnd.fromArray( positions, 3 * i + 3 );

                    var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );

                    if ( distSq > precisionSq ) continue;

                    interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation

                    var distance = raycaster.ray.origin.distanceTo( interRay );

                    if ( distance < raycaster.near || distance > raycaster.far ) continue;

                    intersects.push( {

                        distance: distance,
                        // What do we want? intersection point on the ray or on the segment??
                        // point: raycaster.ray.at( distance ),
                        point: interSegment.clone().applyMatrix4( this.matrixWorld ),
                        index: i,
                        face: null,
                        faceIndex: null,
                        object: this

                    } );

                }

            }

        } else if ( geometry instanceof THREE.Geometry ) {

            var vertices = geometry.vertices;
            var nbVertices = vertices.length;

            for ( var i = 0; i < nbVertices - 1; i += step ) {

                var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );

                if ( distSq > precisionSq ) continue;

                interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation

                var distance = raycaster.ray.origin.distanceTo( interRay );

                if ( distance < raycaster.near || distance > raycaster.far ) continue;

                intersects.push( {

                    distance: distance,
                    // What do we want? intersection point on the ray or on the segment??
                    // point: raycaster.ray.at( distance ),
                    point: interSegment.clone().applyMatrix4( this.matrixWorld ),
                    index: i,
                    face: null,
                    faceIndex: null,
                    object: this

                } );

            }

        }

    };

}() );

THREE.Line.prototype.clone = function () {

    return new this.constructor( this.geometry, this.material ).copy( this );

};

// DEPRECATED

THREE.LineStrip = 0;
THREE.LinePieces = 1;

// File:src/objects/LineSegments.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.LineSegments = function ( geometry, material ) {

    THREE.Line.call( this, geometry, material );

    this.type = 'LineSegments';

};

THREE.LineSegments.prototype = Object.create( THREE.Line.prototype );
THREE.LineSegments.prototype.constructor = THREE.LineSegments;

// File:src/objects/Mesh.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 * @author mikael emtinger / http://gomo.se/
 * @author jonobr1 / http://jonobr1.com/
 */

THREE.Mesh = function ( geometry, material ) {

    THREE.Object3D.call( this );

    this.type = 'Mesh';

    this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
    this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } );

    this.updateMorphTargets();

};

THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype );
THREE.Mesh.prototype.constructor = THREE.Mesh;

THREE.Mesh.prototype.updateMorphTargets = function () {

    if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) {

        this.morphTargetBase = - 1;
        this.morphTargetInfluences = [];
        this.morphTargetDictionary = {};

        for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) {

            this.morphTargetInfluences.push( 0 );
            this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m;

        }

    }

};

THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) {

    if ( this.morphTargetDictionary[ name ] !== undefined ) {

        return this.morphTargetDictionary[ name ];

    }

    console.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' );

    return 0;

};


THREE.Mesh.prototype.raycast = ( function () {

    var inverseMatrix = new THREE.Matrix4();
    var ray = new THREE.Ray();
    var sphere = new THREE.Sphere();

    var vA = new THREE.Vector3();
    var vB = new THREE.Vector3();
    var vC = new THREE.Vector3();

    var tempA = new THREE.Vector3();
    var tempB = new THREE.Vector3();
    var tempC = new THREE.Vector3();

    var uvA = new THREE.Vector2();
    var uvB = new THREE.Vector2();
    var uvC = new THREE.Vector2();

    var barycoord = new THREE.Vector3();

    var intersectionPoint = new THREE.Vector3();
    var intersectionPointWorld = new THREE.Vector3();

    function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) {

        THREE.Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord );

        uv1.multiplyScalar( barycoord.x );
        uv2.multiplyScalar( barycoord.y );
        uv3.multiplyScalar( barycoord.z );

        uv1.add( uv2 ).add( uv3 );

        return uv1.clone();

    }

    function checkIntersection( object, raycaster, ray, pA, pB, pC, point ){

        var intersect;
        var material = object.material;

        if ( material.side === THREE.BackSide ) {

            intersect = ray.intersectTriangle( pC, pB, pA, true, point );

        } else {

            intersect = ray.intersectTriangle( pA, pB, pC, material.side !== THREE.DoubleSide, point );

        }

        if ( intersect === null ) return null;

        intersectionPointWorld.copy( point );
        intersectionPointWorld.applyMatrix4( object.matrixWorld );

        var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld );

        if ( distance < raycaster.near || distance > raycaster.far ) return null;

        return {
            distance: distance,
            point: intersectionPointWorld.clone(),
            object: object
        };

    }

    function checkBufferGeometryIntersection( object, raycaster, ray, positions, uvs, a, b, c ) {

        vA.fromArray( positions, a * 3 );
        vB.fromArray( positions, b * 3 );
        vC.fromArray( positions, c * 3 );

        var intersection = checkIntersection( object, raycaster, ray, vA, vB, vC, intersectionPoint );

        if ( intersection ) {

            if ( uvs ) {

                uvA.fromArray( uvs, a * 2 );
                uvB.fromArray( uvs, b * 2 );
                uvC.fromArray( uvs, c * 2 );

                intersection.uv = uvIntersection( intersectionPoint,  vA, vB, vC,  uvA, uvB, uvC );

            }

            intersection.face = new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) );
            intersection.faceIndex = a;

        }

        return intersection;

    }

    return function raycast( raycaster, intersects ) {

        var geometry = this.geometry;
        var material = this.material;

        if ( material === undefined ) return;

        // Checking boundingSphere distance to ray

        if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();

        var matrixWorld = this.matrixWorld;

        sphere.copy( geometry.boundingSphere );
        sphere.applyMatrix4( matrixWorld );

        if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) return;

        // Check boundingBox before continuing

        inverseMatrix.getInverse( matrixWorld );
        ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );

        if ( geometry.boundingBox !== null ) {

            if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) return;

        }

        var uvs, intersection;

        if ( geometry instanceof THREE.BufferGeometry ) {

            var a, b, c;
            var index = geometry.index;
            var attributes = geometry.attributes;
            var positions = attributes.position.array;

            if ( attributes.uv !== undefined ){

                uvs = attributes.uv.array;

            }

            if ( index !== null ) {

                var indices = index.array;

                for ( var i = 0, l = indices.length; i < l; i += 3 ) {

                    a = indices[ i ];
                    b = indices[ i + 1 ];
                    c = indices[ i + 2 ];

                    intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c );

                    if ( intersection ) {

                        intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics
                        intersects.push( intersection );

                    }

                }

            } else {


                for ( var i = 0, l = positions.length; i < l; i += 9 ) {

                    a = i / 3;
                    b = a + 1;
                    c = a + 2;

                    intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c );

                    if ( intersection ) {

                        intersection.index = a; // triangle number in positions buffer semantics
                        intersects.push( intersection );

                    }

                }

            }

        } else if ( geometry instanceof THREE.Geometry ) {

            var fvA, fvB, fvC;
            var isFaceMaterial = material instanceof THREE.MeshFaceMaterial;
            var materials = isFaceMaterial === true ? material.materials : null;

            var vertices = geometry.vertices;
            var faces = geometry.faces;
            var faceVertexUvs = geometry.faceVertexUvs[ 0 ];
            if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs;

            for ( var f = 0, fl = faces.length; f < fl; f ++ ) {

                var face = faces[ f ];
                var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material;

                if ( faceMaterial === undefined ) continue;

                fvA = vertices[ face.a ];
                fvB = vertices[ face.b ];
                fvC = vertices[ face.c ];

                if ( faceMaterial.morphTargets === true ) {

                    var morphTargets = geometry.morphTargets;
                    var morphInfluences = this.morphTargetInfluences;

                    vA.set( 0, 0, 0 );
                    vB.set( 0, 0, 0 );
                    vC.set( 0, 0, 0 );

                    for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {

                        var influence = morphInfluences[ t ];

                        if ( influence === 0 ) continue;

                        var targets = morphTargets[ t ].vertices;

                        vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence );
                        vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence );
                        vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence );

                    }

                    vA.add( fvA );
                    vB.add( fvB );
                    vC.add( fvC );

                    fvA = vA;
                    fvB = vB;
                    fvC = vC;

                }

                intersection = checkIntersection( this, raycaster, ray, fvA, fvB, fvC, intersectionPoint );

                if ( intersection ) {

                    if ( uvs ) {

                        var uvs_f = uvs[ f ];
                        uvA.copy( uvs_f[ 0 ] );
                        uvB.copy( uvs_f[ 1 ] );
                        uvC.copy( uvs_f[ 2 ] );

                        intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC );

                    }

                    intersection.face = face;
                    intersection.faceIndex = f;
                    intersects.push( intersection );

                }

            }

        }

    };

}() );

THREE.Mesh.prototype.clone = function () {

    return new this.constructor( this.geometry, this.material ).copy( this );

};

// File:src/objects/Bone.js

/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 * @author ikerr / http://verold.com
 */

THREE.Bone = function ( skin ) {

    THREE.Object3D.call( this );

    this.type = 'Bone';

    this.skin = skin;

};

THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
THREE.Bone.prototype.constructor = THREE.Bone;

THREE.Bone.prototype.copy = function ( source ) {

    THREE.Object3D.prototype.copy.call( this, source );

    this.skin = source.skin;

    return this;

};

// File:src/objects/Skeleton.js

/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 * @author michael guerrero / http://realitymeltdown.com
 * @author ikerr / http://verold.com
 */

THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) {

    this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;

    this.identityMatrix = new THREE.Matrix4();

    // copy the bone array

    bones = bones || [];

    this.bones = bones.slice( 0 );

    // create a bone texture or an array of floats

    if ( this.useVertexTexture ) {

        // layout (1 matrix = 4 pixels)
        //      RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
        //  with  8x8  pixel texture max   16 bones * 4 pixels =  (8 * 8)
        //       16x16 pixel texture max   64 bones * 4 pixels = (16 * 16)
        //       32x32 pixel texture max  256 bones * 4 pixels = (32 * 32)
        //       64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)


        var size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix
        size = THREE.Math.nextPowerOfTwo( Math.ceil( size ) );
        size = Math.max( size, 4 );

        this.boneTextureWidth = size;
        this.boneTextureHeight = size;

        this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel
        this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType );

    } else {

        this.boneMatrices = new Float32Array( 16 * this.bones.length );

    }

    // use the supplied bone inverses or calculate the inverses

    if ( boneInverses === undefined ) {

        this.calculateInverses();

    } else {

        if ( this.bones.length === boneInverses.length ) {

            this.boneInverses = boneInverses.slice( 0 );

        } else {

            console.warn( 'THREE.Skeleton bonInverses is the wrong length.' );

            this.boneInverses = [];

            for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {

                this.boneInverses.push( new THREE.Matrix4() );

            }

        }

    }

};

THREE.Skeleton.prototype.calculateInverses = function () {

    this.boneInverses = [];

    for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {

        var inverse = new THREE.Matrix4();

        if ( this.bones[ b ] ) {

            inverse.getInverse( this.bones[ b ].matrixWorld );

        }

        this.boneInverses.push( inverse );

    }

};

THREE.Skeleton.prototype.pose = function () {

    var bone;

    // recover the bind-time world matrices

    for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {

        bone = this.bones[ b ];

        if ( bone ) {

            bone.matrixWorld.getInverse( this.boneInverses[ b ] );

        }

    }

    // compute the local matrices, positions, rotations and scales

    for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {

        bone = this.bones[ b ];

        if ( bone ) {

            if ( bone.parent ) {

                bone.matrix.getInverse( bone.parent.matrixWorld );
                bone.matrix.multiply( bone.matrixWorld );

            } else {

                bone.matrix.copy( bone.matrixWorld );

            }

            bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );

        }

    }

};

THREE.Skeleton.prototype.update = ( function () {

    var offsetMatrix = new THREE.Matrix4();

    return function update() {

        // flatten bone matrices to array

        for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {

            // compute the offset between the current and the original transform

            var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix;

            offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] );
            offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );

        }

        if ( this.useVertexTexture ) {

            this.boneTexture.needsUpdate = true;

        }

    };

} )();

THREE.Skeleton.prototype.clone = function () {

    return new THREE.Skeleton( this.bones, this.boneInverses, this.useVertexTexture );

};

// File:src/objects/SkinnedMesh.js

/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 * @author ikerr / http://verold.com
 */

THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {

    THREE.Mesh.call( this, geometry, material );

    this.type = 'SkinnedMesh';

    this.bindMode = "attached";
    this.bindMatrix = new THREE.Matrix4();
    this.bindMatrixInverse = new THREE.Matrix4();

    // init bones

    // TODO: remove bone creation as there is no reason (other than
    // convenience) for THREE.SkinnedMesh to do this.

    var bones = [];

    if ( this.geometry && this.geometry.bones !== undefined ) {

        var bone, gbone;

        for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) {

            gbone = this.geometry.bones[ b ];

            bone = new THREE.Bone( this );
            bones.push( bone );

            bone.name = gbone.name;
            bone.position.fromArray( gbone.pos );
            bone.quaternion.fromArray( gbone.rotq );
            if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );

        }

        for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) {

            gbone = this.geometry.bones[ b ];

            if ( gbone.parent !== - 1 && gbone.parent !== null) {

                bones[ gbone.parent ].add( bones[ b ] );

            } else {

                this.add( bones[ b ] );

            }

        }

    }

    this.normalizeSkinWeights();

    this.updateMatrixWorld( true );
    this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld );

};


THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
THREE.SkinnedMesh.prototype.constructor = THREE.SkinnedMesh;

THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) {

    this.skeleton = skeleton;

    if ( bindMatrix === undefined ) {

        this.updateMatrixWorld( true );

        this.skeleton.calculateInverses();

        bindMatrix = this.matrixWorld;

    }

    this.bindMatrix.copy( bindMatrix );
    this.bindMatrixInverse.getInverse( bindMatrix );

};

THREE.SkinnedMesh.prototype.pose = function () {

    this.skeleton.pose();

};

THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () {

    if ( this.geometry instanceof THREE.Geometry ) {

        for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) {

            var sw = this.geometry.skinWeights[ i ];

            var scale = 1.0 / sw.lengthManhattan();

            if ( scale !== Infinity ) {

                sw.multiplyScalar( scale );

            } else {

                sw.set( 1 ); // this will be normalized by the shader anyway

            }

        }

    } else {

        // skinning weights assumed to be normalized for THREE.BufferGeometry

    }

};

THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) {

    THREE.Mesh.prototype.updateMatrixWorld.call( this, true );

    if ( this.bindMode === "attached" ) {

        this.bindMatrixInverse.getInverse( this.matrixWorld );

    } else if ( this.bindMode === "detached" ) {

        this.bindMatrixInverse.getInverse( this.bindMatrix );

    } else {

        console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode );

    }

};

THREE.SkinnedMesh.prototype.clone = function() {

    return new this.constructor( this.geometry, this.material, this.useVertexTexture ).copy( this );

};

// File:src/objects/LOD.js

/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 */

THREE.LOD = function () {

    THREE.Object3D.call( this );

    this.type = 'LOD';

    Object.defineProperties( this, {
        levels: {
            enumerable: true,
            value: []
        },
        objects: {
            get: function () {

                console.warn( 'THREE.LOD: .objects has been renamed to .levels.' );
                return this.levels;

            }
        }
    } );

};


THREE.LOD.prototype = Object.create( THREE.Object3D.prototype );
THREE.LOD.prototype.constructor = THREE.LOD;

THREE.LOD.prototype.addLevel = function ( object, distance ) {

    if ( distance === undefined ) distance = 0;

    distance = Math.abs( distance );

    var levels = this.levels;

    for ( var l = 0; l < levels.length; l ++ ) {

        if ( distance < levels[ l ].distance ) {

            break;

        }

    }

    levels.splice( l, 0, { distance: distance, object: object } );

    this.add( object );

};

THREE.LOD.prototype.getObjectForDistance = function ( distance ) {

    var levels = this.levels;

    for ( var i = 1, l = levels.length; i < l; i ++ ) {

        if ( distance < levels[ i ].distance ) {

            break;

        }

    }

    return levels[ i - 1 ].object;

};

THREE.LOD.prototype.raycast = ( function () {

    var matrixPosition = new THREE.Vector3();

    return function raycast( raycaster, intersects ) {

        matrixPosition.setFromMatrixPosition( this.matrixWorld );

        var distance = raycaster.ray.origin.distanceTo( matrixPosition );

        this.getObjectForDistance( distance ).raycast( raycaster, intersects );

    };

}() );

THREE.LOD.prototype.update = function () {

    var v1 = new THREE.Vector3();
    var v2 = new THREE.Vector3();

    return function update( camera ) {

        var levels = this.levels;

        if ( levels.length > 1 ) {

            v1.setFromMatrixPosition( camera.matrixWorld );
            v2.setFromMatrixPosition( this.matrixWorld );

            var distance = v1.distanceTo( v2 );

            levels[ 0 ].object.visible = true;

            for ( var i = 1, l = levels.length; i < l; i ++ ) {

                if ( distance >= levels[ i ].distance ) {

                    levels[ i - 1 ].object.visible = false;
                    levels[ i ].object.visible = true;

                } else {

                    break;

                }

            }

            for ( ; i < l; i ++ ) {

                levels[ i ].object.visible = false;

            }

        }

    };

}();

THREE.LOD.prototype.copy = function ( source ) {

    THREE.Object3D.prototype.copy.call( this, source, false );

    var levels = source.levels;

    for ( var i = 0, l = levels.length; i < l; i ++ ) {

        var level = levels[ i ];

        this.addLevel( level.object.clone(), level.distance );

    }

    return this;

};

THREE.LOD.prototype.toJSON = function ( meta ) {

    var data = THREE.Object3D.prototype.toJSON.call( this, meta );

    data.object.levels = [];

    var levels = this.levels;

    for ( var i = 0, l = levels.length; i < l; i ++ ) {

        var level = levels[ i ];

        data.object.levels.push( {
            object: level.object.uuid,
            distance: level.distance
        } );

    }

    return data;

};

// File:src/objects/Sprite.js

/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Sprite = ( function () {

    var indices = new Uint16Array( [ 0, 1, 2,  0, 2, 3 ] );
    var vertices = new Float32Array( [ - 0.5, - 0.5, 0,   0.5, - 0.5, 0,   0.5, 0.5, 0,   - 0.5, 0.5, 0 ] );
    var uvs = new Float32Array( [ 0, 0,   1, 0,   1, 1,   0, 1 ] );

    var geometry = new THREE.BufferGeometry();
    geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
    geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
    geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );

    return function Sprite( material ) {

        THREE.Object3D.call( this );

        this.type = 'Sprite';

        this.geometry = geometry;
        this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial();

    };

} )();

THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype );
THREE.Sprite.prototype.constructor = THREE.Sprite;

THREE.Sprite.prototype.raycast = ( function () {

    var matrixPosition = new THREE.Vector3();

    return function raycast( raycaster, intersects ) {

        matrixPosition.setFromMatrixPosition( this.matrixWorld );

        var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition );
        var guessSizeSq = this.scale.x * this.scale.y;

        if ( distanceSq > guessSizeSq ) {

            return;

        }

        intersects.push( {

            distance: Math.sqrt( distanceSq ),
            point: this.position,
            face: null,
            object: this

        } );

    };

}() );

THREE.Sprite.prototype.clone = function () {

    return new this.constructor( this.material ).copy( this );

};

// Backwards compatibility

THREE.Particle = THREE.Sprite;

// File:src/objects/LensFlare.js

/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.LensFlare = function ( texture, size, distance, blending, color ) {

    THREE.Object3D.call( this );

    this.lensFlares = [];

    this.positionScreen = new THREE.Vector3();
    this.customUpdateCallback = undefined;

    if ( texture !== undefined ) {

        this.add( texture, size, distance, blending, color );

    }

};

THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype );
THREE.LensFlare.prototype.constructor = THREE.LensFlare;


/*
 * Add: adds another flare
 */

THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) {

    if ( size === undefined ) size = - 1;
    if ( distance === undefined ) distance = 0;
    if ( opacity === undefined ) opacity = 1;
    if ( color === undefined ) color = new THREE.Color( 0xffffff );
    if ( blending === undefined ) blending = THREE.NormalBlending;

    distance = Math.min( distance, Math.max( 0, distance ) );

    this.lensFlares.push( {
        texture: texture,	// THREE.Texture
        size: size, 		// size in pixels (-1 = use texture.width)
        distance: distance, 	// distance (0-1) from light source (0=at light source)
        x: 0, y: 0, z: 0,	// screen position (-1 => 1) z = 0 is in front z = 1 is back
        scale: 1, 		// scale
        rotation: 0, 		// rotation
        opacity: opacity,	// opacity
        color: color,		// color
        blending: blending	// blending
    } );

};

/*
 * Update lens flares update positions on all flares based on the screen position
 * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
 */

THREE.LensFlare.prototype.updateLensFlares = function () {

    var f, fl = this.lensFlares.length;
    var flare;
    var vecX = - this.positionScreen.x * 2;
    var vecY = - this.positionScreen.y * 2;

    for ( f = 0; f < fl; f ++ ) {

        flare = this.lensFlares[ f ];

        flare.x = this.positionScreen.x + vecX * flare.distance;
        flare.y = this.positionScreen.y + vecY * flare.distance;

        flare.wantedRotation = flare.x * Math.PI * 0.25;
        flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;

    }

};

THREE.LensFlare.prototype.copy = function ( source ) {

    THREE.Object3D.prototype.copy.call( this, source );

    this.positionScreen.copy( source.positionScreen );
    this.customUpdateCallback = source.customUpdateCallback;

    for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) {

        this.lensFlares.push( source.lensFlares[ i ] );

    }

    return this;

};

// File:src/scenes/Scene.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.Scene = function () {

    THREE.Object3D.call( this );

    this.type = 'Scene';

    this.fog = null;
    this.overrideMaterial = null;

    this.autoUpdate = true; // checked by the renderer

};

THREE.Scene.prototype = Object.create( THREE.Object3D.prototype );
THREE.Scene.prototype.constructor = THREE.Scene;

THREE.Scene.prototype.copy = function ( source ) {

    THREE.Object3D.prototype.copy.call( this, source );

    if ( source.fog !== null ) this.fog = source.fog.clone();
    if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();

    this.autoUpdate = source.autoUpdate;
    this.matrixAutoUpdate = source.matrixAutoUpdate;

    return this;

};

// File:src/scenes/Fog.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Fog = function ( color, near, far ) {

    this.name = '';

    this.color = new THREE.Color( color );

    this.near = ( near !== undefined ) ? near : 1;
    this.far = ( far !== undefined ) ? far : 1000;

};

THREE.Fog.prototype.clone = function () {

    return new THREE.Fog( this.color.getHex(), this.near, this.far );

};

// File:src/scenes/FogExp2.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.FogExp2 = function ( color, density ) {

    this.name = '';

    this.color = new THREE.Color( color );
    this.density = ( density !== undefined ) ? density : 0.00025;

};

THREE.FogExp2.prototype.clone = function () {

    return new THREE.FogExp2( this.color.getHex(), this.density );

};

// File:src/renderers/shaders/ShaderChunk.js

THREE.ShaderChunk = {};

// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl

THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n	diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl

THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n	uniform sampler2D alphaMap;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl

THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n	if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/aomap_fragment.glsl

THREE.ShaderChunk[ 'aomap_fragment'] = "#ifdef USE_AOMAP\n\n	totalAmbientLight *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl

THREE.ShaderChunk[ 'aomap_pars_fragment'] = "#ifdef USE_AOMAP\n\n	uniform sampler2D aoMap;\n	uniform float aoMapIntensity;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/begin_vertex.glsl

THREE.ShaderChunk[ 'begin_vertex'] = "\nvec3 transformed = vec3( position );\n";

// File:src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl

THREE.ShaderChunk[ 'beginnormal_vertex'] = "\nvec3 objectNormal = vec3( normal );\n";

// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl

THREE.ShaderChunk[ 'bumpmap_pars_fragment'] = "#ifdef USE_BUMPMAP\n\n	uniform sampler2D bumpMap;\n	uniform float bumpScale;\n\n\n\n	vec2 dHdxy_fwd() {\n\n		vec2 dSTdx = dFdx( vUv );\n		vec2 dSTdy = dFdy( vUv );\n\n		float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n		float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n		float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n		return vec2( dBx, dBy );\n\n	}\n\n	vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n		vec3 vSigmaX = dFdx( surf_pos );\n		vec3 vSigmaY = dFdy( surf_pos );\n		vec3 vN = surf_norm;\n		vec3 R1 = cross( vSigmaY, vN );\n		vec3 R2 = cross( vN, vSigmaX );\n\n		float fDet = dot( vSigmaX, R1 );\n\n		vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n		return normalize( abs( fDet ) * surf_norm - vGrad );\n\n	}\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl

THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n	diffuseColor.rgb *= vColor;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl

THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n	varying vec3 vColor;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl

THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n	varying vec3 vColor;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl

THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n	vColor.xyz = color.xyz;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/common.glsl

THREE.ShaderChunk[ 'common'] = "#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\n\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n\n	return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n\n}\n\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n\n	return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n\n}\n\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n	float distance = dot( planeNormal, point - pointOnPlane );\n\n	return - distance * planeNormal + point;\n\n}\n\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n	return sign( dot( point - pointOnPlane, planeNormal ) );\n\n}\n\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n	return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n\n}\n\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n\n	if ( decayExponent > 0.0 ) {\n\n	  return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\n	}\n\n	return 1.0;\n\n}\n\nvec3 F_Schlick( in vec3 specularColor, in float dotLH ) {\n\n\n	float fresnel = exp2( ( -5.55437 * dotLH - 6.98316 ) * dotLH );\n\n	return ( 1.0 - specularColor ) * fresnel + specularColor;\n\n}\n\nfloat G_BlinnPhong_Implicit( /* in float dotNL, in float dotNV */ ) {\n\n\n	return 0.25;\n\n}\n\nfloat D_BlinnPhong( in float shininess, in float dotNH ) {\n\n\n	return ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n\n}\n\nvec3 BRDF_BlinnPhong( in vec3 specularColor, in float shininess, in vec3 normal, in vec3 lightDir, in vec3 viewDir ) {\n\n	vec3 halfDir = normalize( lightDir + viewDir );\n\n	float dotNH = saturate( dot( normal, halfDir ) );\n	float dotLH = saturate( dot( lightDir, halfDir ) );\n\n	vec3 F = F_Schlick( specularColor, dotLH );\n\n	float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );\n\n	float D = D_BlinnPhong( shininess, dotNH );\n\n	return F * G * D;\n\n}\n\nvec3 inputToLinear( in vec3 a ) {\n\n	#ifdef GAMMA_INPUT\n\n		return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n\n	#else\n\n		return a;\n\n	#endif\n\n}\n\nvec3 linearToOutput( in vec3 a ) {\n\n	#ifdef GAMMA_OUTPUT\n\n		return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n\n	#else\n\n		return a;\n\n	#endif\n\n}\n";

// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl

THREE.ShaderChunk[ 'defaultnormal_vertex'] = "#ifdef FLIP_SIDED\n\n	objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n";

// File:src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl

THREE.ShaderChunk[ 'displacementmap_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n	transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl

THREE.ShaderChunk[ 'displacementmap_pars_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n	uniform sampler2D displacementMap;\n	uniform float displacementScale;\n	uniform float displacementBias;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl

THREE.ShaderChunk[ 'emissivemap_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n	vec4 emissiveColor = texture2D( emissiveMap, vUv );\n\n	emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n\n	totalEmissiveLight *= emissiveColor.rgb;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl

THREE.ShaderChunk[ 'emissivemap_pars_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n	uniform sampler2D emissiveMap;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl

THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n		vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n		vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n		#ifdef ENVMAP_MODE_REFLECTION\n\n			vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n		#else\n\n			vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n		#endif\n\n	#else\n\n		vec3 reflectVec = vReflect;\n\n	#endif\n\n	#ifdef DOUBLE_SIDED\n		float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n	#else\n		float flipNormal = 1.0;\n	#endif\n\n	#ifdef ENVMAP_TYPE_CUBE\n		vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n	#elif defined( ENVMAP_TYPE_EQUIREC )\n		vec2 sampleUV;\n		sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n		sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n		vec4 envColor = texture2D( envMap, sampleUV );\n\n	#elif defined( ENVMAP_TYPE_SPHERE )\n		vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n		vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n	#endif\n\n	envColor.xyz = inputToLinear( envColor.xyz );\n\n	#ifdef ENVMAP_BLENDING_MULTIPLY\n\n		outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n	#elif defined( ENVMAP_BLENDING_MIX )\n\n		outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n	#elif defined( ENVMAP_BLENDING_ADD )\n\n		outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n	#endif\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl

THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n	uniform float reflectivity;\n	#ifdef ENVMAP_TYPE_CUBE\n		uniform samplerCube envMap;\n	#else\n		uniform sampler2D envMap;\n	#endif\n	uniform float flipEnvMap;\n\n	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n		uniform float refractionRatio;\n\n	#else\n\n		varying vec3 vReflect;\n\n	#endif\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl

THREE.ShaderChunk[ 'envmap_pars_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n	varying vec3 vReflect;\n\n	uniform float refractionRatio;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl

THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n	vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n	vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\n	#ifdef ENVMAP_MODE_REFLECTION\n\n		vReflect = reflect( cameraToVertex, worldNormal );\n\n	#else\n\n		vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n	#endif\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl

THREE.ShaderChunk[ 'fog_fragment'] = "#ifdef USE_FOG\n\n	#ifdef USE_LOGDEPTHBUF_EXT\n\n		float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n	#else\n\n		float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n	#endif\n\n	#ifdef FOG_EXP2\n\n		float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\n	#else\n\n		float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n	#endif\n	\n	outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl

THREE.ShaderChunk[ 'fog_pars_fragment'] = "#ifdef USE_FOG\n\n	uniform vec3 fogColor;\n\n	#ifdef FOG_EXP2\n\n		uniform float fogDensity;\n\n	#else\n\n		uniform float fogNear;\n		uniform float fogFar;\n	#endif\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/hemilight_fragment.glsl

THREE.ShaderChunk[ 'hemilight_fragment'] = "#if MAX_HEMI_LIGHTS > 0\n\n	for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n		vec3 lightDir = hemisphereLightDirection[ i ];\n\n		float dotProduct = dot( normal, lightDir );\n\n		float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n		vec3 lightColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n		totalAmbientLight += lightColor;\n\n	}\n\n#endif\n\n";

// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl

THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n	totalAmbientLight += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl

THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n	uniform sampler2D lightMap;\n	uniform float lightMapIntensity;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl

THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "#if MAX_DIR_LIGHTS > 0\n\n	uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n	uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n	uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n	uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n	uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n	uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n	uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n	uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n	uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n	uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n	uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n	uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl

THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n	vLightBack = vec3( 0.0 );\n\n#endif\n\nvec3 normal = normalize( transformedNormal );\n\n#if MAX_POINT_LIGHTS > 0\n\n	for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n		vec3 lightColor = pointLightColor[ i ];\n\n		vec3 lVector = pointLightPosition[ i ] - mvPosition.xyz;\n		vec3 lightDir = normalize( lVector );\n\n\n		float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n		float dotProduct = dot( normal, lightDir );\n\n		vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n		#ifdef DOUBLE_SIDED\n\n			vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n		#endif\n\n	}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n	for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n		vec3 lightColor = spotLightColor[ i ];\n\n		vec3 lightPosition = spotLightPosition[ i ];\n		vec3 lVector = lightPosition - mvPosition.xyz;\n		vec3 lightDir = normalize( lVector );\n\n		float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n		if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n			spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n			float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n			attenuation *= spotEffect;\n\n\n			float dotProduct = dot( normal, lightDir );\n\n			vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n			#ifdef DOUBLE_SIDED\n\n				vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n			#endif\n\n		}\n\n	}\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n	for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n		vec3 lightColor = directionalLightColor[ i ];\n\n		vec3 lightDir = directionalLightDirection[ i ];\n\n\n		float dotProduct = dot( normal, lightDir );\n\n		vLightFront += lightColor * saturate( dotProduct );\n\n		#ifdef DOUBLE_SIDED\n\n			vLightBack += lightColor * saturate( - dotProduct );\n\n		#endif\n\n	}\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n	for ( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n		vec3 lightDir = hemisphereLightDirection[ i ];\n\n\n		float dotProduct = dot( normal, lightDir );\n\n		float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n		vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n		#ifdef DOUBLE_SIDED\n\n			float hemiDiffuseWeightBack = - 0.5 * dotProduct + 0.5;\n\n			vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n		#endif\n\n	}\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl

THREE.ShaderChunk[ 'lights_phong_fragment'] = "vec3 viewDir = normalize( vViewPosition );\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n	for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n		vec3 lightColor = pointLightColor[ i ];\n\n		vec3 lightPosition = pointLightPosition[ i ];\n		vec3 lVector = lightPosition + vViewPosition.xyz;\n		vec3 lightDir = normalize( lVector );\n\n\n		float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n\n		float cosineTerm = saturate( dot( normal, lightDir ) );\n\n		totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n		vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n		totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n\n	}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n	for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n		vec3 lightColor = spotLightColor[ i ];\n\n		vec3 lightPosition = spotLightPosition[ i ];\n		vec3 lVector = lightPosition + vViewPosition.xyz;\n		vec3 lightDir = normalize( lVector );\n\n		float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n		if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n			spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n\n			float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n			attenuation *= spotEffect;\n\n\n			float cosineTerm = saturate( dot( normal, lightDir ) );\n\n			totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n\n			vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n			totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n		}\n\n	}\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n	for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n		vec3 lightColor = directionalLightColor[ i ];\n\n		vec3 lightDir = directionalLightDirection[ i ];\n\n\n		float cosineTerm = saturate( dot( normal, lightDir ) );\n\n		totalDiffuseLight += lightColor * cosineTerm;\n\n\n		vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n		totalSpecularLight += brdf * specularStrength * lightColor * cosineTerm;\n\n	}\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl

THREE.ShaderChunk[ 'lights_phong_pars_fragment'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n	uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n	uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n	uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n	uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n	uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n	uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n	uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n	uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n	uniform float pointLightDecay[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n	uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n	uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n	uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n	uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n	varying vec3 vWorldPosition;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n	varying vec3 vNormal;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl

THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n	varying vec3 vWorldPosition;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n	uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl

THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n	vWorldPosition = worldPosition.xyz;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl

THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "\n	outgoingLight = linearToOutput( outgoingLight );\n";

// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl

THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n	gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl

THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n	uniform float logDepthBufFC;\n\n	#ifdef USE_LOGDEPTHBUF_EXT\n\n		varying float vFragDepth;\n\n	#endif\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl

THREE.ShaderChunk[ 'logdepthbuf_pars_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n	#ifdef USE_LOGDEPTHBUF_EXT\n\n		varying float vFragDepth;\n\n	#endif\n\n	uniform float logDepthBufFC;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl

THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n	gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\n	#ifdef USE_LOGDEPTHBUF_EXT\n\n		vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n		gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n	#endif\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl

THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n	vec4 texelColor = texture2D( map, vUv );\n\n	texelColor.xyz = inputToLinear( texelColor.xyz );\n\n	diffuseColor *= texelColor;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl

THREE.ShaderChunk[ 'map_pars_fragment'] = "#ifdef USE_MAP\n\n	uniform sampler2D map;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl

THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n	diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl

THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n	uniform vec4 offsetRepeat;\n	uniform sampler2D map;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl

THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n	objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n	objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n	objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n	objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl

THREE.ShaderChunk[ 'morphtarget_pars_vertex'] = "#ifdef USE_MORPHTARGETS\n\n	#ifndef USE_MORPHNORMALS\n\n	uniform float morphTargetInfluences[ 8 ];\n\n	#else\n\n	uniform float morphTargetInfluences[ 4 ];\n\n	#endif\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl

THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n	transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n	transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n	transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n	transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n	#ifndef USE_MORPHNORMALS\n\n	transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n	transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n	transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n	transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n	#endif\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/normal_phong_fragment.glsl

THREE.ShaderChunk[ 'normal_phong_fragment'] = "#ifndef FLAT_SHADED\n\n	vec3 normal = normalize( vNormal );\n\n	#ifdef DOUBLE_SIDED\n\n		normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n	#endif\n\n#else\n\n	vec3 fdx = dFdx( vViewPosition );\n	vec3 fdy = dFdy( vViewPosition );\n	vec3 normal = normalize( cross( fdx, fdy ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n	normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n	normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n";

// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl

THREE.ShaderChunk[ 'normalmap_pars_fragment'] = "#ifdef USE_NORMALMAP\n\n	uniform sampler2D normalMap;\n	uniform vec2 normalScale;\n\n\n	vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n		vec3 q0 = dFdx( eye_pos.xyz );\n		vec3 q1 = dFdy( eye_pos.xyz );\n		vec2 st0 = dFdx( vUv.st );\n		vec2 st1 = dFdy( vUv.st );\n\n		vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n		vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n		vec3 N = normalize( surf_norm );\n\n		vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n		mapN.xy = normalScale * mapN.xy;\n		mat3 tsn = mat3( S, T, N );\n		return normalize( tsn * mapN );\n\n	}\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/project_vertex.glsl

THREE.ShaderChunk[ 'project_vertex'] = "#ifdef USE_SKINNING\n\n	vec4 mvPosition = modelViewMatrix * skinned;\n\n#else\n\n	vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n";

// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl

THREE.ShaderChunk[ 'shadowmap_fragment'] = "#ifdef USE_SHADOWMAP\n\n	for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n		float texelSizeY =  1.0 / shadowMapSize[ i ].y;\n\n		float shadow = 0.0;\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n		bool isPointLight = shadowDarkness[ i ] < 0.0;\n\n		if ( isPointLight ) {\n\n			float realShadowDarkness = abs( shadowDarkness[ i ] );\n\n			vec3 lightToPosition = vShadowCoord[ i ].xyz;\n\n	#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n			vec3 bd3D = normalize( lightToPosition );\n			float dp = length( lightToPosition );\n\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n\n	#if defined( SHADOWMAP_TYPE_PCF )\n			const float Dr = 1.25;\n	#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n			const float Dr = 2.25;\n	#endif\n\n			float os = Dr *  2.0 * texelSizeY;\n\n			const vec3 Gsd = vec3( - 1, 0, 1 );\n\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xxy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xzy * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.zyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.xyx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxz * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yxx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D + Gsd.yzx * os, texelSizeY ) ), shadowBias[ i ], shadow );\n\n			shadow *= realShadowDarkness * ( 1.0 / 21.0 );\n\n	#else \n			vec3 bd3D = normalize( lightToPosition );\n			float dp = length( lightToPosition );\n\n			adjustShadowValue1K( dp, texture2D( shadowMap[ i ], cubeToUV( bd3D, texelSizeY ) ), shadowBias[ i ], shadow );\n\n			shadow *= realShadowDarkness;\n\n	#endif\n\n		} else {\n\n#endif \n			float texelSizeX =  1.0 / shadowMapSize[ i ].x;\n\n			vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\n			bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n			bool inFrustum = all( inFrustumVec );\n\n			bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n			bool frustumTest = all( frustumTestVec );\n\n			if ( frustumTest ) {\n\n	#if defined( SHADOWMAP_TYPE_PCF )\n\n\n				/*\n					for ( float y = -1.25; y <= 1.25; y += 1.25 )\n						for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n							vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n							float fDepth = unpackDepth( rgbaDepth );\n							if ( fDepth < shadowCoord.z )\n								shadow += 1.0;\n					}\n					shadow /= 9.0;\n				*/\n\n				shadowCoord.z += shadowBias[ i ];\n\n				const float ShadowDelta = 1.0 / 9.0;\n\n				float xPixelOffset = texelSizeX;\n				float yPixelOffset = texelSizeY;\n\n				float dx0 = - 1.25 * xPixelOffset;\n				float dy0 = - 1.25 * yPixelOffset;\n				float dx1 = 1.25 * xPixelOffset;\n				float dy1 = 1.25 * yPixelOffset;\n\n				float fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n				if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n				fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n				if ( fDepth < shadowCoord.z ) shadow += ShadowDelta;\n\n				shadow *= shadowDarkness[ i ];\n\n	#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\n				shadowCoord.z += shadowBias[ i ];\n\n				float xPixelOffset = texelSizeX;\n				float yPixelOffset = texelSizeY;\n\n				float dx0 = - 1.0 * xPixelOffset;\n				float dy0 = - 1.0 * yPixelOffset;\n				float dx1 = 1.0 * xPixelOffset;\n				float dy1 = 1.0 * yPixelOffset;\n\n				mat3 shadowKernel;\n				mat3 depthKernel;\n\n				depthKernel[ 0 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n				depthKernel[ 0 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n				depthKernel[ 0 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n				depthKernel[ 1 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n				depthKernel[ 1 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n				depthKernel[ 1 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n				depthKernel[ 2 ][ 0 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n				depthKernel[ 2 ][ 1 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n				depthKernel[ 2 ][ 2 ] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n				vec3 shadowZ = vec3( shadowCoord.z );\n				shadowKernel[ 0 ] = vec3( lessThan( depthKernel[ 0 ], shadowZ ) );\n				shadowKernel[ 0 ] *= vec3( 0.25 );\n\n				shadowKernel[ 1 ] = vec3( lessThan( depthKernel[ 1 ], shadowZ ) );\n				shadowKernel[ 1 ] *= vec3( 0.25 );\n\n				shadowKernel[ 2 ] = vec3( lessThan( depthKernel[ 2 ], shadowZ ) );\n				shadowKernel[ 2 ] *= vec3( 0.25 );\n\n				vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[ i ].xy );\n\n				shadowKernel[ 0 ] = mix( shadowKernel[ 1 ], shadowKernel[ 0 ], fractionalCoord.x );\n				shadowKernel[ 1 ] = mix( shadowKernel[ 2 ], shadowKernel[ 1 ], fractionalCoord.x );\n\n				vec4 shadowValues;\n				shadowValues.x = mix( shadowKernel[ 0 ][ 1 ], shadowKernel[ 0 ][ 0 ], fractionalCoord.y );\n				shadowValues.y = mix( shadowKernel[ 0 ][ 2 ], shadowKernel[ 0 ][ 1 ], fractionalCoord.y );\n				shadowValues.z = mix( shadowKernel[ 1 ][ 1 ], shadowKernel[ 1 ][ 0 ], fractionalCoord.y );\n				shadowValues.w = mix( shadowKernel[ 1 ][ 2 ], shadowKernel[ 1 ][ 1 ], fractionalCoord.y );\n\n				shadow = dot( shadowValues, vec4( 1.0 ) ) * shadowDarkness[ i ];\n\n	#else \n				shadowCoord.z += shadowBias[ i ];\n\n				vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n				float fDepth = unpackDepth( rgbaDepth );\n\n				if ( fDepth < shadowCoord.z )\n					shadow = shadowDarkness[ i ];\n\n	#endif\n\n			}\n\n#ifdef SHADOWMAP_DEBUG\n\n			if ( inFrustum ) {\n\n				if ( i == 0 ) {\n\n					outgoingLight *= vec3( 1.0, 0.5, 0.0 );\n\n				} else if ( i == 1 ) {\n\n					outgoingLight *= vec3( 0.0, 1.0, 0.8 );\n\n				} else {\n\n					outgoingLight *= vec3( 0.0, 0.5, 1.0 );\n\n				}\n\n			}\n\n#endif\n\n#if defined( POINT_LIGHT_SHADOWS )\n\n		}\n\n#endif\n\n		shadowMask = shadowMask * vec3( 1.0 - shadow );\n\n	}\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl

THREE.ShaderChunk[ 'shadowmap_pars_fragment'] = "#ifdef USE_SHADOWMAP\n\n	uniform sampler2D shadowMap[ MAX_SHADOWS ];\n	uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n	uniform float shadowDarkness[ MAX_SHADOWS ];\n	uniform float shadowBias[ MAX_SHADOWS ];\n\n	varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n	float unpackDepth( const in vec4 rgba_depth ) {\n\n		const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n		float depth = dot( rgba_depth, bit_shift );\n		return depth;\n\n	}\n\n	#if defined(POINT_LIGHT_SHADOWS)\n\n\n		void adjustShadowValue1K( const float testDepth, const vec4 textureData, const float bias, inout float shadowValue ) {\n\n			const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n			if ( testDepth >= dot( textureData, bitSh ) * 1000.0 + bias )\n				shadowValue += 1.0;\n\n		}\n\n\n		vec2 cubeToUV( vec3 v, float texelSizeY ) {\n\n\n			vec3 absV = abs( v );\n\n\n			float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n			absV *= scaleToCube;\n\n\n			v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\n\n\n			vec2 planar = v.xy;\n\n			float almostATexel = 1.5 * texelSizeY;\n			float almostOne = 1.0 - almostATexel;\n\n			if ( absV.z >= almostOne ) {\n\n				if ( v.z > 0.0 )\n					planar.x = 4.0 - v.x;\n\n			} else if ( absV.x >= almostOne ) {\n\n				float signX = sign( v.x );\n				planar.x = v.z * signX + 2.0 * signX;\n\n			} else if ( absV.y >= almostOne ) {\n\n				float signY = sign( v.y );\n				planar.x = v.x + 2.0 * signY + 2.0;\n				planar.y = v.z * signY - 2.0;\n\n			}\n\n\n			return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\n		}\n\n	#endif\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl

THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n	uniform float shadowDarkness[ MAX_SHADOWS ];\n	uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n	varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl

THREE.ShaderChunk[ 'shadowmap_vertex'] = "#ifdef USE_SHADOWMAP\n\n	for ( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n			vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n	}\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl

THREE.ShaderChunk[ 'skinbase_vertex'] = "#ifdef USE_SKINNING\n\n	mat4 boneMatX = getBoneMatrix( skinIndex.x );\n	mat4 boneMatY = getBoneMatrix( skinIndex.y );\n	mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n	mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl

THREE.ShaderChunk[ 'skinning_pars_vertex'] = "#ifdef USE_SKINNING\n\n	uniform mat4 bindMatrix;\n	uniform mat4 bindMatrixInverse;\n\n	#ifdef BONE_TEXTURE\n\n		uniform sampler2D boneTexture;\n		uniform int boneTextureWidth;\n		uniform int boneTextureHeight;\n\n		mat4 getBoneMatrix( const in float i ) {\n\n			float j = i * 4.0;\n			float x = mod( j, float( boneTextureWidth ) );\n			float y = floor( j / float( boneTextureWidth ) );\n\n			float dx = 1.0 / float( boneTextureWidth );\n			float dy = 1.0 / float( boneTextureHeight );\n\n			y = dy * ( y + 0.5 );\n\n			vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n			vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n			vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n			vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n			mat4 bone = mat4( v1, v2, v3, v4 );\n\n			return bone;\n\n		}\n\n	#else\n\n		uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n		mat4 getBoneMatrix( const in float i ) {\n\n			mat4 bone = boneGlobalMatrices[ int(i) ];\n			return bone;\n\n		}\n\n	#endif\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl

THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n	vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\n	vec4 skinned = vec4( 0.0 );\n	skinned += boneMatX * skinVertex * skinWeight.x;\n	skinned += boneMatY * skinVertex * skinWeight.y;\n	skinned += boneMatZ * skinVertex * skinWeight.z;\n	skinned += boneMatW * skinVertex * skinWeight.w;\n	skinned  = bindMatrixInverse * skinned;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl

THREE.ShaderChunk[ 'skinnormal_vertex'] = "#ifdef USE_SKINNING\n\n	mat4 skinMatrix = mat4( 0.0 );\n	skinMatrix += skinWeight.x * boneMatX;\n	skinMatrix += skinWeight.y * boneMatY;\n	skinMatrix += skinWeight.z * boneMatZ;\n	skinMatrix += skinWeight.w * boneMatW;\n	skinMatrix  = bindMatrixInverse * skinMatrix * bindMatrix;\n\n	objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl

THREE.ShaderChunk[ 'specularmap_fragment'] = "float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n	vec4 texelSpecular = texture2D( specularMap, vUv );\n	specularStrength = texelSpecular.r;\n\n#else\n\n	specularStrength = 1.0;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl

THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n	uniform sampler2D specularMap;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/uv2_pars_fragment.glsl

THREE.ShaderChunk[ 'uv2_pars_fragment'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n	varying vec2 vUv2;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/uv2_pars_vertex.glsl

THREE.ShaderChunk[ 'uv2_pars_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n	attribute vec2 uv2;\n	varying vec2 vUv2;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/uv2_vertex.glsl

THREE.ShaderChunk[ 'uv2_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n	vUv2 = uv2;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl

THREE.ShaderChunk[ 'uv_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n	varying vec2 vUv;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl

THREE.ShaderChunk[ 'uv_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n	varying vec2 vUv;\n	uniform vec4 offsetRepeat;\n\n#endif\n";

// File:src/renderers/shaders/ShaderChunk/uv_vertex.glsl

THREE.ShaderChunk[ 'uv_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n	vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif";

// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl

THREE.ShaderChunk[ 'worldpos_vertex'] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n	#ifdef USE_SKINNING\n\n		vec4 worldPosition = modelMatrix * skinned;\n\n	#else\n\n		vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\n	#endif\n\n#endif\n";

// File:src/renderers/shaders/UniformsUtils.js

/**
 * Uniform Utilities
 */

THREE.UniformsUtils = {

    merge: function ( uniforms ) {

        var merged = {};

        for ( var u = 0; u < uniforms.length; u ++ ) {

            var tmp = this.clone( uniforms[ u ] );

            for ( var p in tmp ) {

                merged[ p ] = tmp[ p ];

            }

        }

        return merged;

    },

    clone: function ( uniforms_src ) {

        var uniforms_dst = {};

        for ( var u in uniforms_src ) {

            uniforms_dst[ u ] = {};

            for ( var p in uniforms_src[ u ] ) {

                var parameter_src = uniforms_src[ u ][ p ];

                if ( parameter_src instanceof THREE.Color ||
                    parameter_src instanceof THREE.Vector2 ||
                    parameter_src instanceof THREE.Vector3 ||
                    parameter_src instanceof THREE.Vector4 ||
                    parameter_src instanceof THREE.Matrix3 ||
                    parameter_src instanceof THREE.Matrix4 ||
                    parameter_src instanceof THREE.Texture ) {

                    uniforms_dst[ u ][ p ] = parameter_src.clone();

                } else if ( Array.isArray( parameter_src ) ) {

                    uniforms_dst[ u ][ p ] = parameter_src.slice();

                } else {

                    uniforms_dst[ u ][ p ] = parameter_src;

                }

            }

        }

        return uniforms_dst;

    }

};

// File:src/renderers/shaders/UniformsLib.js

/**
 * Uniforms library for shared webgl shaders
 */

THREE.UniformsLib = {

    common: {

        "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
        "opacity" : { type: "f", value: 1.0 },

        "map" : { type: "t", value: null },
        "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },

        "specularMap" : { type: "t", value: null },
        "alphaMap" : { type: "t", value: null },

        "envMap" : { type: "t", value: null },
        "flipEnvMap" : { type: "f", value: - 1 },
        "reflectivity" : { type: "f", value: 1.0 },
        "refractionRatio" : { type: "f", value: 0.98 }

    },

    aomap: {

        "aoMap" : { type: "t", value: null },
        "aoMapIntensity" : { type: "f", value: 1 },

    },

    lightmap: {

        "lightMap" : { type: "t", value: null },
        "lightMapIntensity" : { type: "f", value: 1 },

    },

    emissivemap: {

        "emissiveMap" : { type: "t", value: null },

    },

    bumpmap: {

        "bumpMap" : { type: "t", value: null },
        "bumpScale" : { type: "f", value: 1 }

    },

    normalmap: {

        "normalMap" : { type: "t", value: null },
        "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }

    },

    displacementmap: {

        "displacementMap" : { type: "t", value: null },
        "displacementScale" : { type: "f", value: 1 },
        "displacementBias" : { type: "f", value: 0 }

    },

    fog : {

        "fogDensity" : { type: "f", value: 0.00025 },
        "fogNear" : { type: "f", value: 1 },
        "fogFar" : { type: "f", value: 2000 },
        "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }

    },

    lights: {

        "ambientLightColor" : { type: "fv", value: [] },

        "directionalLightDirection" : { type: "fv", value: [] },
        "directionalLightColor" : { type: "fv", value: [] },

        "hemisphereLightDirection" : { type: "fv", value: [] },
        "hemisphereLightSkyColor" : { type: "fv", value: [] },
        "hemisphereLightGroundColor" : { type: "fv", value: [] },

        "pointLightColor" : { type: "fv", value: [] },
        "pointLightPosition" : { type: "fv", value: [] },
        "pointLightDistance" : { type: "fv1", value: [] },
        "pointLightDecay" : { type: "fv1", value: [] },

        "spotLightColor" : { type: "fv", value: [] },
        "spotLightPosition" : { type: "fv", value: [] },
        "spotLightDirection" : { type: "fv", value: [] },
        "spotLightDistance" : { type: "fv1", value: [] },
        "spotLightAngleCos" : { type: "fv1", value: [] },
        "spotLightExponent" : { type: "fv1", value: [] },
        "spotLightDecay" : { type: "fv1", value: [] }

    },

    points: {

        "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
        "opacity" : { type: "f", value: 1.0 },
        "size" : { type: "f", value: 1.0 },
        "scale" : { type: "f", value: 1.0 },
        "map" : { type: "t", value: null },
        "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },

        "fogDensity" : { type: "f", value: 0.00025 },
        "fogNear" : { type: "f", value: 1 },
        "fogFar" : { type: "f", value: 2000 },
        "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }

    },

    shadowmap: {

        "shadowMap": { type: "tv", value: [] },
        "shadowMapSize": { type: "v2v", value: [] },

        "shadowBias" : { type: "fv1", value: [] },
        "shadowDarkness": { type: "fv1", value: [] },

        "shadowMatrix" : { type: "m4v", value: [] }

    }

};

// File:src/renderers/shaders/ShaderLib.js

/**
 * Webgl Shader Library for three.js
 *
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 * @author mikael emtinger / http://gomo.se/
 */


THREE.ShaderLib = {

    'basic': {

        uniforms: THREE.UniformsUtils.merge( [

            THREE.UniformsLib[ "common" ],
            THREE.UniformsLib[ "aomap" ],
            THREE.UniformsLib[ "fog" ],
            THREE.UniformsLib[ "shadowmap" ]

        ] ),

        vertexShader: [

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "uv_pars_vertex" ],
            THREE.ShaderChunk[ "uv2_pars_vertex" ],
            THREE.ShaderChunk[ "envmap_pars_vertex" ],
            THREE.ShaderChunk[ "color_pars_vertex" ],
            THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
            THREE.ShaderChunk[ "skinning_pars_vertex" ],
            THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],

            "void main() {",

            THREE.ShaderChunk[ "uv_vertex" ],
            THREE.ShaderChunk[ "uv2_vertex" ],
            THREE.ShaderChunk[ "color_vertex" ],
            THREE.ShaderChunk[ "skinbase_vertex" ],

            "	#ifdef USE_ENVMAP",

            THREE.ShaderChunk[ "beginnormal_vertex" ],
            THREE.ShaderChunk[ "morphnormal_vertex" ],
            THREE.ShaderChunk[ "skinnormal_vertex" ],
            THREE.ShaderChunk[ "defaultnormal_vertex" ],

            "	#endif",

            THREE.ShaderChunk[ "begin_vertex" ],
            THREE.ShaderChunk[ "morphtarget_vertex" ],
            THREE.ShaderChunk[ "skinning_vertex" ],
            THREE.ShaderChunk[ "project_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_vertex" ],

            THREE.ShaderChunk[ "worldpos_vertex" ],
            THREE.ShaderChunk[ "envmap_vertex" ],
            THREE.ShaderChunk[ "shadowmap_vertex" ],

            "}"

        ].join( "\n" ),

        fragmentShader: [

            "uniform vec3 diffuse;",
            "uniform float opacity;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "color_pars_fragment" ],
            THREE.ShaderChunk[ "uv_pars_fragment" ],
            THREE.ShaderChunk[ "uv2_pars_fragment" ],
            THREE.ShaderChunk[ "map_pars_fragment" ],
            THREE.ShaderChunk[ "alphamap_pars_fragment" ],
            THREE.ShaderChunk[ "aomap_pars_fragment" ],
            THREE.ShaderChunk[ "envmap_pars_fragment" ],
            THREE.ShaderChunk[ "fog_pars_fragment" ],
            THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
            THREE.ShaderChunk[ "specularmap_pars_fragment" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],

            "void main() {",

            "	vec3 outgoingLight = vec3( 0.0 );",
            "	vec4 diffuseColor = vec4( diffuse, opacity );",
            "	vec3 totalAmbientLight = vec3( 1.0 );", // hardwired
            "	vec3 shadowMask = vec3( 1.0 );",

            THREE.ShaderChunk[ "logdepthbuf_fragment" ],
            THREE.ShaderChunk[ "map_fragment" ],
            THREE.ShaderChunk[ "color_fragment" ],
            THREE.ShaderChunk[ "alphamap_fragment" ],
            THREE.ShaderChunk[ "alphatest_fragment" ],
            THREE.ShaderChunk[ "specularmap_fragment" ],
            THREE.ShaderChunk[ "aomap_fragment" ],
            THREE.ShaderChunk[ "shadowmap_fragment" ],

            "	outgoingLight = diffuseColor.rgb * totalAmbientLight * shadowMask;",

            THREE.ShaderChunk[ "envmap_fragment" ],

            THREE.ShaderChunk[ "linear_to_gamma_fragment" ],

            THREE.ShaderChunk[ "fog_fragment" ],

            "	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",

            "}"

        ].join( "\n" )

    },

    'lambert': {

        uniforms: THREE.UniformsUtils.merge( [

            THREE.UniformsLib[ "common" ],
            THREE.UniformsLib[ "fog" ],
            THREE.UniformsLib[ "lights" ],
            THREE.UniformsLib[ "shadowmap" ],

            {
                "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }
            }

        ] ),

        vertexShader: [

            "#define LAMBERT",

            "varying vec3 vLightFront;",

            "#ifdef DOUBLE_SIDED",

            "	varying vec3 vLightBack;",

            "#endif",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "uv_pars_vertex" ],
            THREE.ShaderChunk[ "uv2_pars_vertex" ],
            THREE.ShaderChunk[ "envmap_pars_vertex" ],
            THREE.ShaderChunk[ "lights_lambert_pars_vertex" ],
            THREE.ShaderChunk[ "color_pars_vertex" ],
            THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
            THREE.ShaderChunk[ "skinning_pars_vertex" ],
            THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],

            "void main() {",

            THREE.ShaderChunk[ "uv_vertex" ],
            THREE.ShaderChunk[ "uv2_vertex" ],
            THREE.ShaderChunk[ "color_vertex" ],

            THREE.ShaderChunk[ "beginnormal_vertex" ],
            THREE.ShaderChunk[ "morphnormal_vertex" ],
            THREE.ShaderChunk[ "skinbase_vertex" ],
            THREE.ShaderChunk[ "skinnormal_vertex" ],
            THREE.ShaderChunk[ "defaultnormal_vertex" ],

            THREE.ShaderChunk[ "begin_vertex" ],
            THREE.ShaderChunk[ "morphtarget_vertex" ],
            THREE.ShaderChunk[ "skinning_vertex" ],
            THREE.ShaderChunk[ "project_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_vertex" ],

            THREE.ShaderChunk[ "worldpos_vertex" ],
            THREE.ShaderChunk[ "envmap_vertex" ],
            THREE.ShaderChunk[ "lights_lambert_vertex" ],
            THREE.ShaderChunk[ "shadowmap_vertex" ],

            "}"

        ].join( "\n" ),

        fragmentShader: [

            "uniform vec3 diffuse;",
            "uniform vec3 emissive;",
            "uniform float opacity;",

            "uniform vec3 ambientLightColor;",

            "varying vec3 vLightFront;",

            "#ifdef DOUBLE_SIDED",

            "	varying vec3 vLightBack;",

            "#endif",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "color_pars_fragment" ],
            THREE.ShaderChunk[ "uv_pars_fragment" ],
            THREE.ShaderChunk[ "uv2_pars_fragment" ],
            THREE.ShaderChunk[ "map_pars_fragment" ],
            THREE.ShaderChunk[ "alphamap_pars_fragment" ],
            THREE.ShaderChunk[ "envmap_pars_fragment" ],
            THREE.ShaderChunk[ "fog_pars_fragment" ],
            THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
            THREE.ShaderChunk[ "specularmap_pars_fragment" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],

            "void main() {",

            "	vec3 outgoingLight = vec3( 0.0 );",	// outgoing light does not have an alpha, the surface does
            "	vec4 diffuseColor = vec4( diffuse, opacity );",
            "	vec3 totalAmbientLight = ambientLightColor;",
            "	vec3 shadowMask = vec3( 1.0 );",

            THREE.ShaderChunk[ "logdepthbuf_fragment" ],
            THREE.ShaderChunk[ "map_fragment" ],
            THREE.ShaderChunk[ "color_fragment" ],
            THREE.ShaderChunk[ "alphamap_fragment" ],
            THREE.ShaderChunk[ "alphatest_fragment" ],
            THREE.ShaderChunk[ "specularmap_fragment" ],
            THREE.ShaderChunk[ "shadowmap_fragment" ],

            "	#ifdef DOUBLE_SIDED",

            "		if ( gl_FrontFacing )",
            "			outgoingLight += diffuseColor.rgb * ( vLightFront * shadowMask + totalAmbientLight ) + emissive;",
            "		else",
            "			outgoingLight += diffuseColor.rgb * ( vLightBack * shadowMask + totalAmbientLight ) + emissive;",

            "	#else",

            "		outgoingLight += diffuseColor.rgb * ( vLightFront * shadowMask + totalAmbientLight ) + emissive;",

            "	#endif",

            THREE.ShaderChunk[ "envmap_fragment" ],

            THREE.ShaderChunk[ "linear_to_gamma_fragment" ],

            THREE.ShaderChunk[ "fog_fragment" ],

            "	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",

            "}"

        ].join( "\n" )

    },

    'phong': {

        uniforms: THREE.UniformsUtils.merge( [

            THREE.UniformsLib[ "common" ],
            THREE.UniformsLib[ "aomap" ],
            THREE.UniformsLib[ "lightmap" ],
            THREE.UniformsLib[ "emissivemap" ],
            THREE.UniformsLib[ "bumpmap" ],
            THREE.UniformsLib[ "normalmap" ],
            THREE.UniformsLib[ "displacementmap" ],
            THREE.UniformsLib[ "fog" ],
            THREE.UniformsLib[ "lights" ],
            THREE.UniformsLib[ "shadowmap" ],

            {
                "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) },
                "specular" : { type: "c", value: new THREE.Color( 0x111111 ) },
                "shininess": { type: "f", value: 30 }
            }

        ] ),

        vertexShader: [

            "#define PHONG",

            "varying vec3 vViewPosition;",

            "#ifndef FLAT_SHADED",

            "	varying vec3 vNormal;",

            "#endif",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "uv_pars_vertex" ],
            THREE.ShaderChunk[ "uv2_pars_vertex" ],
            THREE.ShaderChunk[ "displacementmap_pars_vertex" ],
            THREE.ShaderChunk[ "envmap_pars_vertex" ],
            THREE.ShaderChunk[ "lights_phong_pars_vertex" ],
            THREE.ShaderChunk[ "color_pars_vertex" ],
            THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
            THREE.ShaderChunk[ "skinning_pars_vertex" ],
            THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],

            "void main() {",

            THREE.ShaderChunk[ "uv_vertex" ],
            THREE.ShaderChunk[ "uv2_vertex" ],
            THREE.ShaderChunk[ "color_vertex" ],

            THREE.ShaderChunk[ "beginnormal_vertex" ],
            THREE.ShaderChunk[ "morphnormal_vertex" ],
            THREE.ShaderChunk[ "skinbase_vertex" ],
            THREE.ShaderChunk[ "skinnormal_vertex" ],
            THREE.ShaderChunk[ "defaultnormal_vertex" ],

            "#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED

            "	vNormal = normalize( transformedNormal );",

            "#endif",

            THREE.ShaderChunk[ "begin_vertex" ],
            THREE.ShaderChunk[ "displacementmap_vertex" ],
            THREE.ShaderChunk[ "morphtarget_vertex" ],
            THREE.ShaderChunk[ "skinning_vertex" ],
            THREE.ShaderChunk[ "project_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_vertex" ],

            "	vViewPosition = - mvPosition.xyz;",

            THREE.ShaderChunk[ "worldpos_vertex" ],
            THREE.ShaderChunk[ "envmap_vertex" ],
            THREE.ShaderChunk[ "lights_phong_vertex" ],
            THREE.ShaderChunk[ "shadowmap_vertex" ],

            "}"

        ].join( "\n" ),

        fragmentShader: [

            "#define PHONG",

            "uniform vec3 diffuse;",
            "uniform vec3 emissive;",
            "uniform vec3 specular;",
            "uniform float shininess;",
            "uniform float opacity;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "color_pars_fragment" ],
            THREE.ShaderChunk[ "uv_pars_fragment" ],
            THREE.ShaderChunk[ "uv2_pars_fragment" ],
            THREE.ShaderChunk[ "map_pars_fragment" ],
            THREE.ShaderChunk[ "alphamap_pars_fragment" ],
            THREE.ShaderChunk[ "aomap_pars_fragment" ],
            THREE.ShaderChunk[ "lightmap_pars_fragment" ],
            THREE.ShaderChunk[ "emissivemap_pars_fragment" ],
            THREE.ShaderChunk[ "envmap_pars_fragment" ],
            THREE.ShaderChunk[ "fog_pars_fragment" ],
            THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
            THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
            THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
            THREE.ShaderChunk[ "normalmap_pars_fragment" ],
            THREE.ShaderChunk[ "specularmap_pars_fragment" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],

            "void main() {",

            "	vec3 outgoingLight = vec3( 0.0 );",
            "	vec4 diffuseColor = vec4( diffuse, opacity );",
            "	vec3 totalAmbientLight = ambientLightColor;",
            "	vec3 totalEmissiveLight = emissive;",
            "	vec3 shadowMask = vec3( 1.0 );",

            THREE.ShaderChunk[ "logdepthbuf_fragment" ],
            THREE.ShaderChunk[ "map_fragment" ],
            THREE.ShaderChunk[ "color_fragment" ],
            THREE.ShaderChunk[ "alphamap_fragment" ],
            THREE.ShaderChunk[ "alphatest_fragment" ],
            THREE.ShaderChunk[ "specularmap_fragment" ],
            THREE.ShaderChunk[ "normal_phong_fragment" ],
            THREE.ShaderChunk[ "lightmap_fragment" ],
            THREE.ShaderChunk[ "hemilight_fragment" ],
            THREE.ShaderChunk[ "aomap_fragment" ],
            THREE.ShaderChunk[ "emissivemap_fragment" ],

            THREE.ShaderChunk[ "lights_phong_fragment" ],
            THREE.ShaderChunk[ "shadowmap_fragment" ],

            "totalDiffuseLight *= shadowMask;",
            "totalSpecularLight *= shadowMask;",

            "#ifdef METAL",

            "	outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) * specular + totalSpecularLight + totalEmissiveLight;",

            "#else",

            "	outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) + totalSpecularLight + totalEmissiveLight;",

            "#endif",

            THREE.ShaderChunk[ "envmap_fragment" ],

            THREE.ShaderChunk[ "linear_to_gamma_fragment" ],

            THREE.ShaderChunk[ "fog_fragment" ],

            "	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",

            "}"

        ].join( "\n" )

    },

    'points': {

        uniforms: THREE.UniformsUtils.merge( [

            THREE.UniformsLib[ "points" ],
            THREE.UniformsLib[ "shadowmap" ]

        ] ),

        vertexShader: [

            "uniform float size;",
            "uniform float scale;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "color_pars_vertex" ],
            THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],

            "void main() {",

            THREE.ShaderChunk[ "color_vertex" ],

            "	vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",

            "	#ifdef USE_SIZEATTENUATION",
            "		gl_PointSize = size * ( scale / length( mvPosition.xyz ) );",
            "	#else",
            "		gl_PointSize = size;",
            "	#endif",

            "	gl_Position = projectionMatrix * mvPosition;",

            THREE.ShaderChunk[ "logdepthbuf_vertex" ],
            THREE.ShaderChunk[ "worldpos_vertex" ],
            THREE.ShaderChunk[ "shadowmap_vertex" ],

            "}"

        ].join( "\n" ),

        fragmentShader: [

            "uniform vec3 psColor;",
            "uniform float opacity;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "color_pars_fragment" ],
            THREE.ShaderChunk[ "map_particle_pars_fragment" ],
            THREE.ShaderChunk[ "fog_pars_fragment" ],
            THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],

            "void main() {",

            "	vec3 outgoingLight = vec3( 0.0 );",
            "	vec4 diffuseColor = vec4( psColor, opacity );",
            "	vec3 shadowMask = vec3( 1.0 );",

            THREE.ShaderChunk[ "logdepthbuf_fragment" ],
            THREE.ShaderChunk[ "map_particle_fragment" ],
            THREE.ShaderChunk[ "color_fragment" ],
            THREE.ShaderChunk[ "alphatest_fragment" ],
            THREE.ShaderChunk[ "shadowmap_fragment" ],

            "	outgoingLight = diffuseColor.rgb * shadowMask;",

            THREE.ShaderChunk[ "fog_fragment" ],

            "	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",

            "}"

        ].join( "\n" )

    },

    'dashed': {

        uniforms: THREE.UniformsUtils.merge( [

            THREE.UniformsLib[ "common" ],
            THREE.UniformsLib[ "fog" ],

            {
                "scale"    : { type: "f", value: 1 },
                "dashSize" : { type: "f", value: 1 },
                "totalSize": { type: "f", value: 2 }
            }

        ] ),

        vertexShader: [

            "uniform float scale;",
            "attribute float lineDistance;",

            "varying float vLineDistance;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "color_pars_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],

            "void main() {",

            THREE.ShaderChunk[ "color_vertex" ],

            "	vLineDistance = scale * lineDistance;",

            "	vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
            "	gl_Position = projectionMatrix * mvPosition;",

            THREE.ShaderChunk[ "logdepthbuf_vertex" ],

            "}"

        ].join( "\n" ),

        fragmentShader: [

            "uniform vec3 diffuse;",
            "uniform float opacity;",

            "uniform float dashSize;",
            "uniform float totalSize;",

            "varying float vLineDistance;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "color_pars_fragment" ],
            THREE.ShaderChunk[ "fog_pars_fragment" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],

            "void main() {",

            "	if ( mod( vLineDistance, totalSize ) > dashSize ) {",

            "		discard;",

            "	}",

            "	vec3 outgoingLight = vec3( 0.0 );",
            "	vec4 diffuseColor = vec4( diffuse, opacity );",

            THREE.ShaderChunk[ "logdepthbuf_fragment" ],
            THREE.ShaderChunk[ "color_fragment" ],

            "	outgoingLight = diffuseColor.rgb;", // simple shader

            THREE.ShaderChunk[ "fog_fragment" ],

            "	gl_FragColor = vec4( outgoingLight, diffuseColor.a );",

            "}"

        ].join( "\n" )

    },

    'depth': {

        uniforms: {

            "mNear": { type: "f", value: 1.0 },
            "mFar" : { type: "f", value: 2000.0 },
            "opacity" : { type: "f", value: 1.0 }

        },

        vertexShader: [

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],

            "void main() {",

            THREE.ShaderChunk[ "begin_vertex" ],
            THREE.ShaderChunk[ "morphtarget_vertex" ],
            THREE.ShaderChunk[ "project_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_vertex" ],

            "}"

        ].join( "\n" ),

        fragmentShader: [

            "uniform float mNear;",
            "uniform float mFar;",
            "uniform float opacity;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],

            "void main() {",

            THREE.ShaderChunk[ "logdepthbuf_fragment" ],

            "	#ifdef USE_LOGDEPTHBUF_EXT",

            "		float depth = gl_FragDepthEXT / gl_FragCoord.w;",

            "	#else",

            "		float depth = gl_FragCoord.z / gl_FragCoord.w;",

            "	#endif",

            "	float color = 1.0 - smoothstep( mNear, mFar, depth );",
            "	gl_FragColor = vec4( vec3( color ), opacity );",

            "}"

        ].join( "\n" )

    },

    'normal': {

        uniforms: {

            "opacity" : { type: "f", value: 1.0 }

        },

        vertexShader: [

            "varying vec3 vNormal;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],

            "void main() {",

            "	vNormal = normalize( normalMatrix * normal );",

            THREE.ShaderChunk[ "begin_vertex" ],
            THREE.ShaderChunk[ "morphtarget_vertex" ],
            THREE.ShaderChunk[ "project_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_vertex" ],

            "}"

        ].join( "\n" ),

        fragmentShader: [

            "uniform float opacity;",
            "varying vec3 vNormal;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],

            "void main() {",

            "	gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",

            THREE.ShaderChunk[ "logdepthbuf_fragment" ],

            "}"

        ].join( "\n" )

    },

    /* -------------------------------------------------------------------------
	//	Cube map shader
	 ------------------------------------------------------------------------- */

    'cube': {

        uniforms: { "tCube": { type: "t", value: null },
            "tFlip": { type: "f", value: - 1 } },

        vertexShader: [

            "varying vec3 vWorldPosition;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],

            "void main() {",

            "	vWorldPosition = transformDirection( position, modelMatrix );",

            "	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",

            THREE.ShaderChunk[ "logdepthbuf_vertex" ],

            "}"

        ].join( "\n" ),

        fragmentShader: [

            "uniform samplerCube tCube;",
            "uniform float tFlip;",

            "varying vec3 vWorldPosition;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],

            "void main() {",

            "	gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",

            THREE.ShaderChunk[ "logdepthbuf_fragment" ],

            "}"

        ].join( "\n" )

    },

    /* -------------------------------------------------------------------------
	//	Cube map shader
	 ------------------------------------------------------------------------- */

    'equirect': {

        uniforms: { "tEquirect": { type: "t", value: null },
            "tFlip": { type: "f", value: - 1 } },

        vertexShader: [

            "varying vec3 vWorldPosition;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],

            "void main() {",

            "	vWorldPosition = transformDirection( position, modelMatrix );",

            "	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",

            THREE.ShaderChunk[ "logdepthbuf_vertex" ],

            "}"

        ].join( "\n" ),

        fragmentShader: [

            "uniform sampler2D tEquirect;",
            "uniform float tFlip;",

            "varying vec3 vWorldPosition;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],

            "void main() {",

            // "	gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
            "vec3 direction = normalize( vWorldPosition );",
            "vec2 sampleUV;",
            "sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );",
            "sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;",
            "gl_FragColor = texture2D( tEquirect, sampleUV );",

            THREE.ShaderChunk[ "logdepthbuf_fragment" ],

            "}"

        ].join( "\n" )

    },

    /* Depth encoding into RGBA texture
	 *
	 * based on SpiderGL shadow map example
	 * http://spidergl.org/example.php?id=6
	 *
	 * originally from
	 * http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD
	 *
	 * see also
	 * http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/
	 */

    'depthRGBA': {

        uniforms: {},

        vertexShader: [

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
            THREE.ShaderChunk[ "skinning_pars_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ],

            "void main() {",

            THREE.ShaderChunk[ "skinbase_vertex" ],

            THREE.ShaderChunk[ "begin_vertex" ],
            THREE.ShaderChunk[ "morphtarget_vertex" ],
            THREE.ShaderChunk[ "skinning_vertex" ],
            THREE.ShaderChunk[ "project_vertex" ],
            THREE.ShaderChunk[ "logdepthbuf_vertex" ],

            "}"

        ].join( "\n" ),

        fragmentShader: [

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],

            "vec4 pack_depth( const in float depth ) {",

            "	const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );",
            "	const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );",
            "	vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", // "	vec4 res = fract( depth * bit_shift );",
            "	res -= res.xxyz * bit_mask;",
            "	return res;",

            "}",

            "void main() {",

            THREE.ShaderChunk[ "logdepthbuf_fragment" ],

            "	#ifdef USE_LOGDEPTHBUF_EXT",

            "		gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );",

            "	#else",

            "		gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );",

            "	#endif",

            //"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );",
            //"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );",
            //"gl_FragData[ 0 ] = pack_depth( z );",
            //"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );",

            "}"

        ].join( "\n" )

    },


    'distanceRGBA': {

        uniforms: {

            "lightPos": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) }

        },

        vertexShader: [

            "varying vec4 vWorldPosition;",

            THREE.ShaderChunk[ "common" ],
            THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
            THREE.ShaderChunk[ "skinning_pars_vertex" ],

            "void main() {",

            THREE.ShaderChunk[ "skinbase_vertex" ],
            THREE.ShaderChunk[ "begin_vertex" ],
            THREE.ShaderChunk[ "morphtarget_vertex" ],
            THREE.ShaderChunk[ "skinning_vertex" ],
            THREE.ShaderChunk[ "project_vertex" ],
            THREE.ShaderChunk[ "worldpos_vertex" ],

            "vWorldPosition = worldPosition;",

            "}"

        ].join( "\n" ),

        fragmentShader: [

            "uniform vec3 lightPos;",
            "varying vec4 vWorldPosition;",

            THREE.ShaderChunk[ "common" ],

            "vec4 pack1K ( float depth ) {",

            "   depth /= 1000.0;",
            "   const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );",
            "	const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );",
            "	vec4 res = fract( depth * bitSh );",
            "	res -= res.xxyz * bitMsk;",
            "	return res; ",

            "}",

            "float unpack1K ( vec4 color ) {",

            "	const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );",
            "	return dot( color, bitSh ) * 1000.0;",

            "}",

            "void main () {",

            "	gl_FragColor = pack1K( length( vWorldPosition.xyz - lightPos.xyz ) );",

            "}"

        ].join( "\n" )

    }

};

// File:src/renderers/WebGLRenderer.js

/**
 * @author supereggbert / http://www.paulbrunt.co.uk/
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 * @author szimek / https://github.com/szimek/
 */

THREE.WebGLRenderer = function ( parameters ) {

    console.log( 'THREE.WebGLRenderer', THREE.REVISION );

    parameters = parameters || {};

    var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ),
        _context = parameters.context !== undefined ? parameters.context : null,

        _width = _canvas.width,
        _height = _canvas.height,

        pixelRatio = 1,

        _alpha = parameters.alpha !== undefined ? parameters.alpha : false,
        _depth = parameters.depth !== undefined ? parameters.depth : true,
        _stencil = parameters.stencil !== undefined ? parameters.stencil : true,
        _antialias = parameters.antialias !== undefined ? parameters.antialias : false,
        _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
        _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,

        _clearColor = new THREE.Color( 0x000000 ),
        _clearAlpha = 0;

    var lights = [];

    var opaqueObjects = [];
    var opaqueObjectsLastIndex = - 1;
    var transparentObjects = [];
    var transparentObjectsLastIndex = - 1;

    var morphInfluences = new Float32Array( 8 );


    var sprites = [];
    var lensFlares = [];

    // public properties

    this.domElement = _canvas;
    this.context = null;

    // clearing

    this.autoClear = true;
    this.autoClearColor = true;
    this.autoClearDepth = true;
    this.autoClearStencil = true;

    // scene graph

    this.sortObjects = true;

    // physically based shading

    this.gammaFactor = 2.0;	// for backwards compatibility
    this.gammaInput = false;
    this.gammaOutput = false;

    // morphs

    this.maxMorphTargets = 8;
    this.maxMorphNormals = 4;

    // flags

    this.autoScaleCubemaps = true;

    // internal properties

    var _this = this,

        // internal state cache

        _currentProgram = null,
        _currentFramebuffer = null,
        _currentMaterialId = - 1,
        _currentGeometryProgram = '',
        _currentCamera = null,

        _usedTextureUnits = 0,

        _viewportX = 0,
        _viewportY = 0,
        _viewportWidth = _canvas.width,
        _viewportHeight = _canvas.height,
        _currentWidth = 0,
        _currentHeight = 0,

        // frustum

        _frustum = new THREE.Frustum(),

        // camera matrices cache

        _projScreenMatrix = new THREE.Matrix4(),

        _vector3 = new THREE.Vector3(),

        // light arrays cache

        _direction = new THREE.Vector3(),

        _lightsNeedUpdate = true,

        _lights = {

            ambient: [ 0, 0, 0 ],
            directional: { length: 0, colors: [], positions: [] },
            point: { length: 0, colors: [], positions: [], distances: [], decays: [] },
            spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [], decays: [] },
            hemi: { length: 0, skyColors: [], groundColors: [], positions: [] }

        },

        // info

        _infoMemory = {

            geometries: 0,
            textures: 0

        },

        _infoRender = {

            calls: 0,
            vertices: 0,
            faces: 0,
            points: 0

        };

    this.info = {

        render: _infoRender,
        memory: _infoMemory,
        programs: null

    };


    // initialize

    var _gl;

    try {

        var attributes = {
            alpha: _alpha,
            depth: _depth,
            stencil: _stencil,
            antialias: _antialias,
            premultipliedAlpha: _premultipliedAlpha,
            preserveDrawingBuffer: _preserveDrawingBuffer
        };

        _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes );

        if ( _gl === null ) {

            if ( _canvas.getContext( 'webgl' ) !== null ) {

                throw 'Error creating WebGL context with your selected attributes.';

            } else {

                throw 'Error creating WebGL context.';

            }

        }

        _canvas.addEventListener( 'webglcontextlost', onContextLost, false );

    } catch ( error ) {

        console.error( 'THREE.WebGLRenderer: ' + error );

    }

    var extensions = new THREE.WebGLExtensions( _gl );

    extensions.get( 'OES_texture_float' );
    extensions.get( 'OES_texture_float_linear' );
    extensions.get( 'OES_texture_half_float' );
    extensions.get( 'OES_texture_half_float_linear' );
    extensions.get( 'OES_standard_derivatives' );
    extensions.get( 'ANGLE_instanced_arrays' );

    if ( extensions.get( 'OES_element_index_uint' ) ) {

        THREE.BufferGeometry.MaxIndex = 4294967296;

    }

    var capabilities = new THREE.WebGLCapabilities( _gl, extensions, parameters );

    var state = new THREE.WebGLState( _gl, extensions, paramThreeToGL );
    var properties = new THREE.WebGLProperties();
    var objects = new THREE.WebGLObjects( _gl, properties, this.info );
    var programCache = new THREE.WebGLPrograms( this, capabilities );

    this.info.programs = programCache.programs;

    var bufferRenderer = new THREE.WebGLBufferRenderer( _gl, extensions, _infoRender );
    var indexedBufferRenderer = new THREE.WebGLIndexedBufferRenderer( _gl, extensions, _infoRender );

    //

    function glClearColor( r, g, b, a ) {

        if ( _premultipliedAlpha === true ) {

            r *= a; g *= a; b *= a;

        }

        _gl.clearColor( r, g, b, a );

    }

    function setDefaultGLState() {

        state.init();

        _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );

        glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );

    }

    function resetGLState() {

        _currentProgram = null;
        _currentCamera = null;

        _currentGeometryProgram = '';
        _currentMaterialId = - 1;

        _lightsNeedUpdate = true;

        state.reset();

    }

    setDefaultGLState();

    this.context = _gl;
    this.capabilities = capabilities;
    this.extensions = extensions;
    this.state = state;

    // shadow map

    var shadowMap = new THREE.WebGLShadowMap( this, lights, objects );

    this.shadowMap = shadowMap;


    // Plugins

    var spritePlugin = new THREE.SpritePlugin( this, sprites );
    var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares );

    // API

    this.getContext = function () {

        return _gl;

    };

    this.getContextAttributes = function () {

        return _gl.getContextAttributes();

    };

    this.forceContextLoss = function () {

        extensions.get( 'WEBGL_lose_context' ).loseContext();

    };

    this.getMaxAnisotropy = ( function () {

        var value;

        return function getMaxAnisotropy() {

            if ( value !== undefined ) return value;

            var extension = extensions.get( 'EXT_texture_filter_anisotropic' );

            if ( extension !== null ) {

                value = _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );

            } else {

                value = 0;

            }

            return value;

        }

    } )();

    this.getPrecision = function () {

        return capabilities.precision;

    };

    this.getPixelRatio = function () {

        return pixelRatio;

    };

    this.setPixelRatio = function ( value ) {

        if ( value !== undefined ) pixelRatio = value;

    };

    this.getSize = function () {

        return {
            width: _width,
            height: _height
        };

    };

    this.setSize = function ( width, height, updateStyle ) {

        _width = width;
        _height = height;

        _canvas.width = width * pixelRatio;
        _canvas.height = height * pixelRatio;

        if ( updateStyle !== false ) {

            _canvas.style.width = width + 'px';
            _canvas.style.height = height + 'px';

        }

        this.setViewport( 0, 0, width, height );

    };

    this.setViewport = function ( x, y, width, height ) {

        _viewportX = x * pixelRatio;
        _viewportY = y * pixelRatio;

        _viewportWidth = width * pixelRatio;
        _viewportHeight = height * pixelRatio;

        _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );

    };

    this.getViewport = function ( dimensions ) {

        dimensions.x = _viewportX / pixelRatio;
        dimensions.y = _viewportY / pixelRatio;

        dimensions.z = _viewportWidth / pixelRatio;
        dimensions.w = _viewportHeight / pixelRatio;

    };

    this.setScissor = function ( x, y, width, height ) {

        _gl.scissor(
            x * pixelRatio,
            y * pixelRatio,
            width * pixelRatio,
            height * pixelRatio
        );

    };

    this.enableScissorTest = function ( boolean ) {

        state.setScissorTest( boolean );

    };

    // Clearing

    this.getClearColor = function () {

        return _clearColor;

    };

    this.setClearColor = function ( color, alpha ) {

        _clearColor.set( color );

        _clearAlpha = alpha !== undefined ? alpha : 1;

        glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );

    };

    this.getClearAlpha = function () {

        return _clearAlpha;

    };

    this.setClearAlpha = function ( alpha ) {

        _clearAlpha = alpha;

        glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );

    };

    this.clear = function ( color, depth, stencil ) {

        var bits = 0;

        if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;
        if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;
        if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;

        _gl.clear( bits );

    };

    this.clearColor = function () {

        _gl.clear( _gl.COLOR_BUFFER_BIT );

    };

    this.clearDepth = function () {

        _gl.clear( _gl.DEPTH_BUFFER_BIT );

    };

    this.clearStencil = function () {

        _gl.clear( _gl.STENCIL_BUFFER_BIT );

    };

    this.clearTarget = function ( renderTarget, color, depth, stencil ) {

        this.setRenderTarget( renderTarget );
        this.clear( color, depth, stencil );

    };

    // Reset

    this.resetGLState = resetGLState;

    this.dispose = function() {

        _canvas.removeEventListener( 'webglcontextlost', onContextLost, false );

    };

    // Events

    function onContextLost( event ) {

        event.preventDefault();

        resetGLState();
        setDefaultGLState();

        properties.clear();

    };

    function onTextureDispose( event ) {

        var texture = event.target;

        texture.removeEventListener( 'dispose', onTextureDispose );

        deallocateTexture( texture );

        _infoMemory.textures --;


    }

    function onRenderTargetDispose( event ) {

        var renderTarget = event.target;

        renderTarget.removeEventListener( 'dispose', onRenderTargetDispose );

        deallocateRenderTarget( renderTarget );

        _infoMemory.textures --;

    }

    function onMaterialDispose( event ) {

        var material = event.target;

        material.removeEventListener( 'dispose', onMaterialDispose );

        deallocateMaterial( material );

    }

    // Buffer deallocation

    function deallocateTexture( texture ) {

        var textureProperties = properties.get( texture );

        if ( texture.image && textureProperties.__image__webglTextureCube ) {

            // cube texture

            _gl.deleteTexture( textureProperties.__image__webglTextureCube );

        } else {

            // 2D texture

            if ( textureProperties.__webglInit === undefined ) return;

            _gl.deleteTexture( textureProperties.__webglTexture );

        }

        // remove all webgl properties
        properties.delete( texture );

    }

    function deallocateRenderTarget( renderTarget ) {

        var renderTargetProperties = properties.get( renderTarget );
        var textureProperties = properties.get( renderTarget.texture );

        if ( ! renderTarget || textureProperties.__webglTexture === undefined ) return;

        _gl.deleteTexture( textureProperties.__webglTexture );

        if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {

            for ( var i = 0; i < 6; i ++ ) {

                _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] );
                _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer[ i ] );

            }

        } else {

            _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer );
            _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer );

        }

        properties.delete( renderTarget.texture );
        properties.delete( renderTarget );

    }

    function deallocateMaterial( material ) {

        releaseMaterialProgramReference( material );

        properties.delete( material );

    }


    function releaseMaterialProgramReference( material ) {

        var programInfo = properties.get( material ).program;

        material.program = undefined;

        if ( programInfo !== undefined ) {

            programCache.releaseProgram( programInfo );

        }

    }

    // Buffer rendering

    this.renderBufferImmediate = function ( object, program, material ) {

        state.initAttributes();

        var buffers = properties.get( object );

        if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer();
        if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer();
        if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer();
        if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer();

        var attributes = program.getAttributes();

        if ( object.hasPositions ) {

            _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position );
            _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );

            state.enableAttribute( attributes.position );
            _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );

        }

        if ( object.hasNormals ) {

            _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal );

            if ( material.type !== 'MeshPhongMaterial' && material.shading === THREE.FlatShading ) {

                for ( var i = 0, l = object.count * 3; i < l; i += 9 ) {

                    var array = object.normalArray;

                    var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3;
                    var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3;
                    var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3;

                    array[ i + 0 ] = nx;
                    array[ i + 1 ] = ny;
                    array[ i + 2 ] = nz;

                    array[ i + 3 ] = nx;
                    array[ i + 4 ] = ny;
                    array[ i + 5 ] = nz;

                    array[ i + 6 ] = nx;
                    array[ i + 7 ] = ny;
                    array[ i + 8 ] = nz;

                }

            }

            _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );

            state.enableAttribute( attributes.normal );

            _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 );

        }

        if ( object.hasUvs && material.map ) {

            _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv );
            _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );

            state.enableAttribute( attributes.uv );

            _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 );

        }

        if ( object.hasColors && material.vertexColors !== THREE.NoColors ) {

            _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color );
            _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );

            state.enableAttribute( attributes.color );

            _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 );

        }

        state.disableUnusedAttributes();

        _gl.drawArrays( _gl.TRIANGLES, 0, object.count );

        object.count = 0;

    };

    this.renderBufferDirect = function ( camera, lights, fog, geometry, material, object, group ) {

        setMaterial( material );

        var program = setProgram( camera, lights, fog, material, object );

        var updateBuffers = false;
        var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe;

        if ( geometryProgram !== _currentGeometryProgram ) {

            _currentGeometryProgram = geometryProgram;
            updateBuffers = true;

        }

        // morph targets

        var morphTargetInfluences = object.morphTargetInfluences;

        if ( morphTargetInfluences !== undefined ) {

            var activeInfluences = [];

            for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) {

                var influence = morphTargetInfluences[ i ];
                activeInfluences.push( [ influence, i ] );

            }

            activeInfluences.sort( numericalSort );

            if ( activeInfluences.length > 8 ) {

                activeInfluences.length = 8;

            }

            var morphAttributes = geometry.morphAttributes;

            for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) {

                var influence = activeInfluences[ i ];
                morphInfluences[ i ] = influence[ 0 ];

                if ( influence[ 0 ] !== 0 ) {

                    var index = influence[ 1 ];

                    if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] );
                    if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] );

                } else {

                    if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i );
                    if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i );

                }

            }

            var uniforms = program.getUniforms();

            if ( uniforms.morphTargetInfluences !== null ) {

                _gl.uniform1fv( uniforms.morphTargetInfluences, morphInfluences );

            }

            updateBuffers = true;

        }

        //

        var index = geometry.index;
        var position = geometry.attributes.position;

        if ( material.wireframe === true ) {

            index = objects.getWireframeAttribute( geometry );

        }

        var renderer;

        if ( index !== null ) {

            renderer = indexedBufferRenderer;
            renderer.setIndex( index );

        } else {

            renderer = bufferRenderer;

        }

        if ( updateBuffers ) {

            setupVertexAttributes( material, program, geometry );

            if ( index !== null ) {

                _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) );

            }

        }

        //

        var dataStart = 0;
        var dataCount = Infinity;

        if ( index !== null ) {

            dataCount = index.count

        } else if ( position !== undefined ) {

            dataCount = position.count;

        }

        var rangeStart = geometry.drawRange.start;
        var rangeCount = geometry.drawRange.count;

        var groupStart = group !== null ? group.start : 0;
        var groupCount = group !== null ? group.count : Infinity;

        var drawStart = Math.max( dataStart, rangeStart, groupStart );
        var drawEnd = Math.min( dataStart + dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;

        var drawCount = Math.max( 0, drawEnd - drawStart + 1 );

        //

        if ( object instanceof THREE.Mesh ) {

            if ( material.wireframe === true ) {

                state.setLineWidth( material.wireframeLinewidth * pixelRatio );
                renderer.setMode( _gl.LINES );

            } else {

                renderer.setMode( _gl.TRIANGLES );

            }

            if ( geometry instanceof THREE.InstancedBufferGeometry && geometry.maxInstancedCount > 0 ) {

                renderer.renderInstances( geometry );

            } else {

                renderer.render( drawStart, drawCount );

            }

        } else if ( object instanceof THREE.Line ) {

            var lineWidth = material.linewidth;

            if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material

            state.setLineWidth( lineWidth * pixelRatio );

            if ( object instanceof THREE.LineSegments ) {

                renderer.setMode( _gl.LINES );

            } else {

                renderer.setMode( _gl.LINE_STRIP );

            }

            renderer.render( drawStart, drawCount );

        } else if ( object instanceof THREE.Points ) {

            renderer.setMode( _gl.POINTS );
            renderer.render( drawStart, drawCount );

        }

    };

    function setupVertexAttributes( material, program, geometry, startIndex ) {

        var extension;

        if ( geometry instanceof THREE.InstancedBufferGeometry ) {

            extension = extensions.get( 'ANGLE_instanced_arrays' );

            if ( extension === null ) {

                console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
                return;

            }

        }

        if ( startIndex === undefined ) startIndex = 0;

        state.initAttributes();

        var geometryAttributes = geometry.attributes;

        var programAttributes = program.getAttributes();

        var materialDefaultAttributeValues = material.defaultAttributeValues;

        for ( var name in programAttributes ) {

            var programAttribute = programAttributes[ name ];

            if ( programAttribute >= 0 ) {

                var geometryAttribute = geometryAttributes[ name ];

                if ( geometryAttribute !== undefined ) {

                    var size = geometryAttribute.itemSize;
                    var buffer = objects.getAttributeBuffer( geometryAttribute );

                    if ( geometryAttribute instanceof THREE.InterleavedBufferAttribute ) {

                        var data = geometryAttribute.data;
                        var stride = data.stride;
                        var offset = geometryAttribute.offset;

                        if ( data instanceof THREE.InstancedInterleavedBuffer ) {

                            state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute, extension );

                            if ( geometry.maxInstancedCount === undefined ) {

                                geometry.maxInstancedCount = data.meshPerAttribute * data.count;

                            }

                        } else {

                            state.enableAttribute( programAttribute );

                        }

                        _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
                        _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, stride * data.array.BYTES_PER_ELEMENT, ( startIndex * stride + offset ) * data.array.BYTES_PER_ELEMENT );

                    } else {

                        if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) {

                            state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute, extension );

                            if ( geometry.maxInstancedCount === undefined ) {

                                geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;

                            }

                        } else {

                            state.enableAttribute( programAttribute );

                        }

                        _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
                        _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32

                    }

                } else if ( materialDefaultAttributeValues !== undefined ) {

                    var value = materialDefaultAttributeValues[ name ];

                    if ( value !== undefined ) {

                        switch ( value.length ) {

                            case 2:
                                _gl.vertexAttrib2fv( programAttribute, value );
                                break;

                            case 3:
                                _gl.vertexAttrib3fv( programAttribute, value );
                                break;

                            case 4:
                                _gl.vertexAttrib4fv( programAttribute, value );
                                break;

                            default:
                                _gl.vertexAttrib1fv( programAttribute, value );

                        }

                    }

                }

            }

        }

        state.disableUnusedAttributes();

    }

    // Sorting

    function numericalSort ( a, b ) {

        return b[ 0 ] - a[ 0 ];

    }

    function painterSortStable ( a, b ) {

        if ( a.object.renderOrder !== b.object.renderOrder ) {

            return a.object.renderOrder - b.object.renderOrder;

        } else if ( a.material.id !== b.material.id ) {

            return a.material.id - b.material.id;

        } else if ( a.z !== b.z ) {

            return a.z - b.z;

        } else {

            return a.id - b.id;

        }

    }

    function reversePainterSortStable ( a, b ) {

        if ( a.object.renderOrder !== b.object.renderOrder ) {

            return a.object.renderOrder - b.object.renderOrder;

        } if ( a.z !== b.z ) {

            return b.z - a.z;

        } else {

            return a.id - b.id;

        }

    }

    // Rendering

    this.render = function ( scene, camera, renderTarget, forceClear ) {

        if ( camera instanceof THREE.Camera === false ) {

            console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
            return;

        }

        var fog = scene.fog;

        // reset caching for this frame

        _currentGeometryProgram = '';
        _currentMaterialId = - 1;
        _currentCamera = null;
        _lightsNeedUpdate = true;

        // update scene graph

        if ( scene.autoUpdate === true ) scene.updateMatrixWorld();

        // update camera matrices and frustum

        if ( camera.parent === null ) camera.updateMatrixWorld();

        camera.matrixWorldInverse.getInverse( camera.matrixWorld );

        _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
        _frustum.setFromMatrix( _projScreenMatrix );

        lights.length = 0;

        opaqueObjectsLastIndex = - 1;
        transparentObjectsLastIndex = - 1;

        sprites.length = 0;
        lensFlares.length = 0;

        projectObject( scene, camera );

        opaqueObjects.length = opaqueObjectsLastIndex + 1;
        transparentObjects.length = transparentObjectsLastIndex + 1;

        if ( _this.sortObjects === true ) {

            opaqueObjects.sort( painterSortStable );
            transparentObjects.sort( reversePainterSortStable );

        }

        //

        shadowMap.render( scene );

        //

        _infoRender.calls = 0;
        _infoRender.vertices = 0;
        _infoRender.faces = 0;
        _infoRender.points = 0;

        this.setRenderTarget( renderTarget );

        if ( this.autoClear || forceClear ) {

            this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil );

        }

        //

        if ( scene.overrideMaterial ) {

            var overrideMaterial = scene.overrideMaterial;

            renderObjects( opaqueObjects, camera, lights, fog, overrideMaterial );
            renderObjects( transparentObjects, camera, lights, fog, overrideMaterial );

        } else {

            // opaque pass (front-to-back order)

            state.setBlending( THREE.NoBlending );
            renderObjects( opaqueObjects, camera, lights, fog );

            // transparent pass (back-to-front order)

            renderObjects( transparentObjects, camera, lights, fog );

        }

        // custom render plugins (post pass)

        spritePlugin.render( scene, camera );
        lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight );

        // Generate mipmap if we're using any kind of mipmap filtering

        if ( renderTarget ) {

            var texture = renderTarget.texture;
            var isTargetPowerOfTwo = isPowerOfTwo( renderTarget );
            if ( texture.generateMipmaps && isTargetPowerOfTwo && texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) {

                updateRenderTargetMipmap( renderTarget );

            }

        }

        // Ensure depth buffer writing is enabled so it can be cleared on next render

        state.setDepthTest( true );
        state.setDepthWrite( true );
        state.setColorWrite( true );

        // _gl.finish();

    };

    function pushRenderItem( object, geometry, material, z, group ) {

        var array, index;

        // allocate the next position in the appropriate array

        if ( material.transparent ) {

            array = transparentObjects;
            index = ++ transparentObjectsLastIndex;

        } else {

            array = opaqueObjects;
            index = ++ opaqueObjectsLastIndex;

        }

        // recycle existing render item or grow the array

        var renderItem = array[ index ];

        if ( renderItem !== undefined ) {

            renderItem.id = object.id;
            renderItem.object = object;
            renderItem.geometry = geometry;
            renderItem.material = material;
            renderItem.z = _vector3.z;
            renderItem.group = group;

        } else {

            renderItem = {
                id: object.id,
                object: object,
                geometry: geometry,
                material: material,
                z: _vector3.z,
                group: group
            };

            // assert( index === array.length );
            array.push( renderItem );

        }

    }

    function projectObject( object, camera ) {

        if ( object.visible === false ) return;

        if ( ( object.channels.mask & camera.channels.mask ) !== 0 ) {

            if ( object instanceof THREE.Light ) {

                lights.push( object );

            } else if ( object instanceof THREE.Sprite ) {

                sprites.push( object );

            } else if ( object instanceof THREE.LensFlare ) {

                lensFlares.push( object );

            } else if ( object instanceof THREE.ImmediateRenderObject ) {

                if ( _this.sortObjects === true ) {

                    _vector3.setFromMatrixPosition( object.matrixWorld );
                    _vector3.applyProjection( _projScreenMatrix );

                }

                pushRenderItem( object, null, object.material, _vector3.z, null );

            } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) {

                if ( object instanceof THREE.SkinnedMesh ) {

                    object.skeleton.update();

                }

                if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {

                    var material = object.material;

                    if ( material.visible === true ) {

                        if ( _this.sortObjects === true ) {

                            _vector3.setFromMatrixPosition( object.matrixWorld );
                            _vector3.applyProjection( _projScreenMatrix );

                        }

                        var geometry = objects.update( object );

                        if ( material instanceof THREE.MeshFaceMaterial ) {

                            var groups = geometry.groups;
                            var materials = material.materials;

                            for ( var i = 0, l = groups.length; i < l; i ++ ) {

                                var group = groups[ i ];
                                var groupMaterial = materials[ group.materialIndex ];

                                if ( groupMaterial.visible === true ) {

                                    pushRenderItem( object, geometry, groupMaterial, _vector3.z, group );

                                }

                            }

                        } else {

                            pushRenderItem( object, geometry, material, _vector3.z, null );

                        }

                    }

                }

            }

        }

        var children = object.children;

        for ( var i = 0, l = children.length; i < l; i ++ ) {

            projectObject( children[ i ], camera );

        }

    }

    function renderObjects( renderList, camera, lights, fog, overrideMaterial ) {

        for ( var i = 0, l = renderList.length; i < l; i ++ ) {

            var renderItem = renderList[ i ];

            var object = renderItem.object;
            var geometry = renderItem.geometry;
            var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;
            var group = renderItem.group;

            object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
            object.normalMatrix.getNormalMatrix( object.modelViewMatrix );

            if ( object instanceof THREE.ImmediateRenderObject ) {

                setMaterial( material );

                var program = setProgram( camera, lights, fog, material, object );

                _currentGeometryProgram = '';

                object.render( function ( object ) {

                    _this.renderBufferImmediate( object, program, material );

                } );

            } else {

                _this.renderBufferDirect( camera, lights, fog, geometry, material, object, group );

            }

        }

    }

    function initMaterial( material, lights, fog, object ) {

        var materialProperties = properties.get( material );

        var parameters = programCache.getParameters( material, lights, fog, object );
        var code = programCache.getProgramCode( material, parameters );

        var program = materialProperties.program;
        var programChange = true;

        if ( program === undefined ) {

            // new material
            material.addEventListener( 'dispose', onMaterialDispose );

        } else if ( program.code !== code ) {

            // changed glsl or parameters
            releaseMaterialProgramReference( material );

        } else if ( parameters.shaderID !== undefined ) {

            // same glsl and uniform list
            return;

        } else {

            // only rebuild uniform list
            programChange = false;

        }

        if ( programChange ) {

            if ( parameters.shaderID ) {

                var shader = THREE.ShaderLib[ parameters.shaderID ];

                materialProperties.__webglShader = {
                    name: material.type,
                    uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
                    vertexShader: shader.vertexShader,
                    fragmentShader: shader.fragmentShader
                };

            } else {

                materialProperties.__webglShader = {
                    name: material.type,
                    uniforms: material.uniforms,
                    vertexShader: material.vertexShader,
                    fragmentShader: material.fragmentShader
                };

            }

            material.__webglShader = materialProperties.__webglShader;

            program = programCache.acquireProgram( material, parameters, code );

            materialProperties.program = program;
            material.program = program;

        }

        var attributes = program.getAttributes();

        if ( material.morphTargets ) {

            material.numSupportedMorphTargets = 0;

            for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {

                if ( attributes[ 'morphTarget' + i ] >= 0 ) {

                    material.numSupportedMorphTargets ++;

                }

            }

        }

        if ( material.morphNormals ) {

            material.numSupportedMorphNormals = 0;

            for ( i = 0; i < _this.maxMorphNormals; i ++ ) {

                if ( attributes[ 'morphNormal' + i ] >= 0 ) {

                    material.numSupportedMorphNormals ++;

                }

            }

        }

        materialProperties.uniformsList = [];

        var uniformLocations = materialProperties.program.getUniforms();

        for ( var u in materialProperties.__webglShader.uniforms ) {

            var location = uniformLocations[ u ];

            if ( location ) {

                materialProperties.uniformsList.push( [ materialProperties.__webglShader.uniforms[ u ], location ] );

            }

        }

    }

    function setMaterial( material ) {

        setMaterialFaces( material );

        if ( material.transparent === true ) {

            state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha );

        } else {

            state.setBlending( THREE.NoBlending );

        }

        state.setDepthFunc( material.depthFunc );
        state.setDepthTest( material.depthTest );
        state.setDepthWrite( material.depthWrite );
        state.setColorWrite( material.colorWrite );
        state.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );

    }

    function setMaterialFaces( material ) {

        material.side !== THREE.DoubleSide ? state.enable( _gl.CULL_FACE ) : state.disable( _gl.CULL_FACE );
        state.setFlipSided( material.side === THREE.BackSide );

    }

    function setProgram( camera, lights, fog, material, object ) {

        _usedTextureUnits = 0;

        var materialProperties = properties.get( material );

        if ( material.needsUpdate || ! materialProperties.program ) {

            initMaterial( material, lights, fog, object );
            material.needsUpdate = false;

        }

        var refreshProgram = false;
        var refreshMaterial = false;
        var refreshLights = false;

        var program = materialProperties.program,
            p_uniforms = program.getUniforms(),
            m_uniforms = materialProperties.__webglShader.uniforms;

        if ( program.id !== _currentProgram ) {

            _gl.useProgram( program.program );
            _currentProgram = program.id;

            refreshProgram = true;
            refreshMaterial = true;
            refreshLights = true;

        }

        if ( material.id !== _currentMaterialId ) {

            if ( _currentMaterialId === - 1 ) refreshLights = true;
            _currentMaterialId = material.id;

            refreshMaterial = true;

        }

        if ( refreshProgram || camera !== _currentCamera ) {

            _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements );

            if ( capabilities.logarithmicDepthBuffer ) {

                _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );

            }


            if ( camera !== _currentCamera ) _currentCamera = camera;

            // load material specific uniforms
            // (shader material also gets them for the sake of genericity)

            if ( material instanceof THREE.ShaderMaterial ||
                material instanceof THREE.MeshPhongMaterial ||
                material.envMap ) {

                if ( p_uniforms.cameraPosition !== undefined ) {

                    _vector3.setFromMatrixPosition( camera.matrixWorld );
                    _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z );

                }

            }

            if ( material instanceof THREE.MeshPhongMaterial ||
                material instanceof THREE.MeshLambertMaterial ||
                material instanceof THREE.MeshBasicMaterial ||
                material instanceof THREE.ShaderMaterial ||
                material.skinning ) {

                if ( p_uniforms.viewMatrix !== undefined ) {

                    _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements );

                }

            }

        }

        // skinning uniforms must be set even if material didn't change
        // auto-setting of texture unit for bone texture must go before other textures
        // not sure why, but otherwise weird things happen

        if ( material.skinning ) {

            if ( object.bindMatrix && p_uniforms.bindMatrix !== undefined ) {

                _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements );

            }

            if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== undefined ) {

                _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements );

            }

            if ( capabilities.floatVertexTextures && object.skeleton && object.skeleton.useVertexTexture ) {

                if ( p_uniforms.boneTexture !== undefined ) {

                    var textureUnit = getTextureUnit();

                    _gl.uniform1i( p_uniforms.boneTexture, textureUnit );
                    _this.setTexture( object.skeleton.boneTexture, textureUnit );

                }

                if ( p_uniforms.boneTextureWidth !== undefined ) {

                    _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth );

                }

                if ( p_uniforms.boneTextureHeight !== undefined ) {

                    _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight );

                }

            } else if ( object.skeleton && object.skeleton.boneMatrices ) {

                if ( p_uniforms.boneGlobalMatrices !== undefined ) {

                    _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices );

                }

            }

        }

        if ( refreshMaterial ) {

            // refresh uniforms common to several materials

            if ( fog && material.fog ) {

                refreshUniformsFog( m_uniforms, fog );

            }

            if ( material instanceof THREE.MeshPhongMaterial ||
                material instanceof THREE.MeshLambertMaterial ||
                material.lights ) {

                if ( _lightsNeedUpdate ) {

                    refreshLights = true;
                    setupLights( lights, camera );
                    _lightsNeedUpdate = false;

                }

                if ( refreshLights ) {

                    refreshUniformsLights( m_uniforms, _lights );
                    markUniformsLightsNeedsUpdate( m_uniforms, true );

                } else {

                    markUniformsLightsNeedsUpdate( m_uniforms, false );

                }

            }

            if ( material instanceof THREE.MeshBasicMaterial ||
                material instanceof THREE.MeshLambertMaterial ||
                material instanceof THREE.MeshPhongMaterial ) {

                refreshUniformsCommon( m_uniforms, material );

            }

            // refresh single material specific uniforms

            if ( material instanceof THREE.LineBasicMaterial ) {

                refreshUniformsLine( m_uniforms, material );

            } else if ( material instanceof THREE.LineDashedMaterial ) {

                refreshUniformsLine( m_uniforms, material );
                refreshUniformsDash( m_uniforms, material );

            } else if ( material instanceof THREE.PointsMaterial ) {

                refreshUniformsParticle( m_uniforms, material );

            } else if ( material instanceof THREE.MeshPhongMaterial ) {

                refreshUniformsPhong( m_uniforms, material );

            } else if ( material instanceof THREE.MeshDepthMaterial ) {

                m_uniforms.mNear.value = camera.near;
                m_uniforms.mFar.value = camera.far;
                m_uniforms.opacity.value = material.opacity;

            } else if ( material instanceof THREE.MeshNormalMaterial ) {

                m_uniforms.opacity.value = material.opacity;

            }

            if ( object.receiveShadow && ! material._shadowPass ) {

                refreshUniformsShadow( m_uniforms, lights, camera );

            }

            // load common uniforms

            loadUniformsGeneric( materialProperties.uniformsList );

        }

        loadUniformsMatrices( p_uniforms, object );

        if ( p_uniforms.modelMatrix !== undefined ) {

            _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements );

        }

        return program;

    }

    // Uniforms (refresh uniforms objects)

    function refreshUniformsCommon ( uniforms, material ) {

        uniforms.opacity.value = material.opacity;

        uniforms.diffuse.value = material.color;

        if ( material.emissive ) {

            uniforms.emissive.value = material.emissive;

        }

        uniforms.map.value = material.map;
        uniforms.specularMap.value = material.specularMap;
        uniforms.alphaMap.value = material.alphaMap;

        if ( material.aoMap ) {

            uniforms.aoMap.value = material.aoMap;
            uniforms.aoMapIntensity.value = material.aoMapIntensity;

        }

        // uv repeat and offset setting priorities
        // 1. color map
        // 2. specular map
        // 3. normal map
        // 4. bump map
        // 5. alpha map
        // 6. emissive map

        var uvScaleMap;

        if ( material.map ) {

            uvScaleMap = material.map;

        } else if ( material.specularMap ) {

            uvScaleMap = material.specularMap;

        } else if ( material.displacementMap ) {

            uvScaleMap = material.displacementMap;

        } else if ( material.normalMap ) {

            uvScaleMap = material.normalMap;

        } else if ( material.bumpMap ) {

            uvScaleMap = material.bumpMap;

        } else if ( material.alphaMap ) {

            uvScaleMap = material.alphaMap;

        } else if ( material.emissiveMap ) {

            uvScaleMap = material.emissiveMap;

        }

        if ( uvScaleMap !== undefined ) {

            if ( uvScaleMap instanceof THREE.WebGLRenderTarget ) uvScaleMap = uvScaleMap.texture;
            var offset = uvScaleMap.offset;
            var repeat = uvScaleMap.repeat;

            uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );

        }

        uniforms.envMap.value = material.envMap;
        uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1;

        uniforms.reflectivity.value = material.reflectivity;
        uniforms.refractionRatio.value = material.refractionRatio;

    }

    function refreshUniformsLine ( uniforms, material ) {

        uniforms.diffuse.value = material.color;
        uniforms.opacity.value = material.opacity;

    }

    function refreshUniformsDash ( uniforms, material ) {

        uniforms.dashSize.value = material.dashSize;
        uniforms.totalSize.value = material.dashSize + material.gapSize;
        uniforms.scale.value = material.scale;

    }

    function refreshUniformsParticle ( uniforms, material ) {

        uniforms.psColor.value = material.color;
        uniforms.opacity.value = material.opacity;
        uniforms.size.value = material.size;
        uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this.

        uniforms.map.value = material.map;

        if ( material.map !== null ) {

            var offset = material.map.offset;
            var repeat = material.map.repeat;

            uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );

        }

    }

    function refreshUniformsFog ( uniforms, fog ) {

        uniforms.fogColor.value = fog.color;

        if ( fog instanceof THREE.Fog ) {

            uniforms.fogNear.value = fog.near;
            uniforms.fogFar.value = fog.far;

        } else if ( fog instanceof THREE.FogExp2 ) {

            uniforms.fogDensity.value = fog.density;

        }

    }

    function refreshUniformsPhong ( uniforms, material ) {

        uniforms.specular.value = material.specular;
        uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )

        if ( material.lightMap ) {

            uniforms.lightMap.value = material.lightMap;
            uniforms.lightMapIntensity.value = material.lightMapIntensity;

        }

        if ( material.emissiveMap ) {

            uniforms.emissiveMap.value = material.emissiveMap;

        }

        if ( material.bumpMap ) {

            uniforms.bumpMap.value = material.bumpMap;
            uniforms.bumpScale.value = material.bumpScale;

        }

        if ( material.normalMap ) {

            uniforms.normalMap.value = material.normalMap;
            uniforms.normalScale.value.copy( material.normalScale );

        }

        if ( material.displacementMap ) {

            uniforms.displacementMap.value = material.displacementMap;
            uniforms.displacementScale.value = material.displacementScale;
            uniforms.displacementBias.value = material.displacementBias;

        }

    }

    function refreshUniformsLights ( uniforms, lights ) {

        uniforms.ambientLightColor.value = lights.ambient;

        uniforms.directionalLightColor.value = lights.directional.colors;
        uniforms.directionalLightDirection.value = lights.directional.positions;

        uniforms.pointLightColor.value = lights.point.colors;
        uniforms.pointLightPosition.value = lights.point.positions;
        uniforms.pointLightDistance.value = lights.point.distances;
        uniforms.pointLightDecay.value = lights.point.decays;

        uniforms.spotLightColor.value = lights.spot.colors;
        uniforms.spotLightPosition.value = lights.spot.positions;
        uniforms.spotLightDistance.value = lights.spot.distances;
        uniforms.spotLightDirection.value = lights.spot.directions;
        uniforms.spotLightAngleCos.value = lights.spot.anglesCos;
        uniforms.spotLightExponent.value = lights.spot.exponents;
        uniforms.spotLightDecay.value = lights.spot.decays;

        uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors;
        uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors;
        uniforms.hemisphereLightDirection.value = lights.hemi.positions;

    }

    // If uniforms are marked as clean, they don't need to be loaded to the GPU.

    function markUniformsLightsNeedsUpdate ( uniforms, value ) {

        uniforms.ambientLightColor.needsUpdate = value;

        uniforms.directionalLightColor.needsUpdate = value;
        uniforms.directionalLightDirection.needsUpdate = value;

        uniforms.pointLightColor.needsUpdate = value;
        uniforms.pointLightPosition.needsUpdate = value;
        uniforms.pointLightDistance.needsUpdate = value;
        uniforms.pointLightDecay.needsUpdate = value;

        uniforms.spotLightColor.needsUpdate = value;
        uniforms.spotLightPosition.needsUpdate = value;
        uniforms.spotLightDistance.needsUpdate = value;
        uniforms.spotLightDirection.needsUpdate = value;
        uniforms.spotLightAngleCos.needsUpdate = value;
        uniforms.spotLightExponent.needsUpdate = value;
        uniforms.spotLightDecay.needsUpdate = value;

        uniforms.hemisphereLightSkyColor.needsUpdate = value;
        uniforms.hemisphereLightGroundColor.needsUpdate = value;
        uniforms.hemisphereLightDirection.needsUpdate = value;

    }

    function refreshUniformsShadow ( uniforms, lights, camera ) {

        if ( uniforms.shadowMatrix ) {

            var j = 0;

            for ( var i = 0, il = lights.length; i < il; i ++ ) {

                var light = lights[ i ];

                if ( light.castShadow === true ) {

                    if ( light instanceof THREE.PointLight || light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) {

                        var shadow = light.shadow;

                        if ( light instanceof THREE.PointLight ) {

                            // for point lights we set the shadow matrix to be a translation-only matrix
                            // equal to inverse of the light's position
                            _vector3.setFromMatrixPosition( light.matrixWorld ).negate();
                            shadow.matrix.identity().setPosition( _vector3 );

                            // for point lights we set the sign of the shadowDarkness uniform to be negative
                            uniforms.shadowDarkness.value[ j ] = - shadow.darkness;

                        } else {

                            uniforms.shadowDarkness.value[ j ] = shadow.darkness;

                        }

                        uniforms.shadowMatrix.value[ j ] = shadow.matrix;
                        uniforms.shadowMap.value[ j ] = shadow.map;
                        uniforms.shadowMapSize.value[ j ] = shadow.mapSize;
                        uniforms.shadowBias.value[ j ] = shadow.bias;

                        j ++;

                    }

                }

            }

        }

    }

    // Uniforms (load to GPU)

    function loadUniformsMatrices ( uniforms, object ) {

        _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object.modelViewMatrix.elements );

        if ( uniforms.normalMatrix ) {

            _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object.normalMatrix.elements );

        }

    }

    function getTextureUnit() {

        var textureUnit = _usedTextureUnits;

        if ( textureUnit >= capabilities.maxTextures ) {

            console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures );

        }

        _usedTextureUnits += 1;

        return textureUnit;

    }

    function loadUniformsGeneric ( uniforms ) {

        var texture, textureUnit;

        for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) {

            var uniform = uniforms[ j ][ 0 ];

            // needsUpdate property is not added to all uniforms.
            if ( uniform.needsUpdate === false ) continue;

            var type = uniform.type;
            var value = uniform.value;
            var location = uniforms[ j ][ 1 ];

            switch ( type ) {

                case '1i':
                    _gl.uniform1i( location, value );
                    break;

                case '1f':
                    _gl.uniform1f( location, value );
                    break;

                case '2f':
                    _gl.uniform2f( location, value[ 0 ], value[ 1 ] );
                    break;

                case '3f':
                    _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] );
                    break;

                case '4f':
                    _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] );
                    break;

                case '1iv':
                    _gl.uniform1iv( location, value );
                    break;

                case '3iv':
                    _gl.uniform3iv( location, value );
                    break;

                case '1fv':
                    _gl.uniform1fv( location, value );
                    break;

                case '2fv':
                    _gl.uniform2fv( location, value );
                    break;

                case '3fv':
                    _gl.uniform3fv( location, value );
                    break;

                case '4fv':
                    _gl.uniform4fv( location, value );
                    break;

                case 'Matrix3fv':
                    _gl.uniformMatrix3fv( location, false, value );
                    break;

                case 'Matrix4fv':
                    _gl.uniformMatrix4fv( location, false, value );
                    break;

                //

                case 'i':

                    // single integer
                    _gl.uniform1i( location, value );

                    break;

                case 'f':

                    // single float
                    _gl.uniform1f( location, value );

                    break;

                case 'v2':

                    // single THREE.Vector2
                    _gl.uniform2f( location, value.x, value.y );

                    break;

                case 'v3':

                    // single THREE.Vector3
                    _gl.uniform3f( location, value.x, value.y, value.z );

                    break;

                case 'v4':

                    // single THREE.Vector4
                    _gl.uniform4f( location, value.x, value.y, value.z, value.w );

                    break;

                case 'c':

                    // single THREE.Color
                    _gl.uniform3f( location, value.r, value.g, value.b );

                    break;

                case 'iv1':

                    // flat array of integers (JS or typed array)
                    _gl.uniform1iv( location, value );

                    break;

                case 'iv':

                    // flat array of integers with 3 x N size (JS or typed array)
                    _gl.uniform3iv( location, value );

                    break;

                case 'fv1':

                    // flat array of floats (JS or typed array)
                    _gl.uniform1fv( location, value );

                    break;

                case 'fv':

                    // flat array of floats with 3 x N size (JS or typed array)
                    _gl.uniform3fv( location, value );

                    break;

                case 'v2v':

                    // array of THREE.Vector2

                    if ( uniform._array === undefined ) {

                        uniform._array = new Float32Array( 2 * value.length );

                    }

                    for ( var i = 0, i2 = 0, il = value.length; i < il; i ++, i2 += 2 ) {

                        uniform._array[ i2 + 0 ] = value[ i ].x;
                        uniform._array[ i2 + 1 ] = value[ i ].y;

                    }

                    _gl.uniform2fv( location, uniform._array );

                    break;

                case 'v3v':

                    // array of THREE.Vector3

                    if ( uniform._array === undefined ) {

                        uniform._array = new Float32Array( 3 * value.length );

                    }

                    for ( var i = 0, i3 = 0, il = value.length; i < il; i ++, i3 += 3 ) {

                        uniform._array[ i3 + 0 ] = value[ i ].x;
                        uniform._array[ i3 + 1 ] = value[ i ].y;
                        uniform._array[ i3 + 2 ] = value[ i ].z;

                    }

                    _gl.uniform3fv( location, uniform._array );

                    break;

                case 'v4v':

                    // array of THREE.Vector4

                    if ( uniform._array === undefined ) {

                        uniform._array = new Float32Array( 4 * value.length );

                    }

                    for ( var i = 0, i4 = 0, il = value.length; i < il; i ++, i4 += 4 ) {

                        uniform._array[ i4 + 0 ] = value[ i ].x;
                        uniform._array[ i4 + 1 ] = value[ i ].y;
                        uniform._array[ i4 + 2 ] = value[ i ].z;
                        uniform._array[ i4 + 3 ] = value[ i ].w;

                    }

                    _gl.uniform4fv( location, uniform._array );

                    break;

                case 'm3':

                    // single THREE.Matrix3
                    _gl.uniformMatrix3fv( location, false, value.elements );

                    break;

                case 'm3v':

                    // array of THREE.Matrix3

                    if ( uniform._array === undefined ) {

                        uniform._array = new Float32Array( 9 * value.length );

                    }

                    for ( var i = 0, il = value.length; i < il; i ++ ) {

                        value[ i ].flattenToArrayOffset( uniform._array, i * 9 );

                    }

                    _gl.uniformMatrix3fv( location, false, uniform._array );

                    break;

                case 'm4':

                    // single THREE.Matrix4
                    _gl.uniformMatrix4fv( location, false, value.elements );

                    break;

                case 'm4v':

                    // array of THREE.Matrix4

                    if ( uniform._array === undefined ) {

                        uniform._array = new Float32Array( 16 * value.length );

                    }

                    for ( var i = 0, il = value.length; i < il; i ++ ) {

                        value[ i ].flattenToArrayOffset( uniform._array, i * 16 );

                    }

                    _gl.uniformMatrix4fv( location, false, uniform._array );

                    break;

                case 't':

                    // single THREE.Texture (2d or cube)

                    texture = value;
                    textureUnit = getTextureUnit();

                    _gl.uniform1i( location, textureUnit );

                    if ( ! texture ) continue;

                    if ( texture instanceof THREE.CubeTexture ||
                        ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) {

                        // CompressedTexture can have Array in image :/

                        setCubeTexture( texture, textureUnit );

                    } else if ( texture instanceof THREE.WebGLRenderTargetCube ) {

                        setCubeTextureDynamic( texture.texture, textureUnit );

                    } else if ( texture instanceof THREE.WebGLRenderTarget ) {

                        _this.setTexture( texture.texture, textureUnit );

                    } else {

                        _this.setTexture( texture, textureUnit );

                    }

                    break;

                case 'tv':

                    // array of THREE.Texture (2d or cube)

                    if ( uniform._array === undefined ) {

                        uniform._array = [];

                    }

                    for ( var i = 0, il = uniform.value.length; i < il; i ++ ) {

                        uniform._array[ i ] = getTextureUnit();

                    }

                    _gl.uniform1iv( location, uniform._array );

                    for ( var i = 0, il = uniform.value.length; i < il; i ++ ) {

                        texture = uniform.value[ i ];
                        textureUnit = uniform._array[ i ];

                        if ( ! texture ) continue;

                        if ( texture instanceof THREE.CubeTexture ||
                            ( texture.image instanceof Array && texture.image.length === 6 ) ) {

                            // CompressedTexture can have Array in image :/

                            setCubeTexture( texture, textureUnit );

                        } else if ( texture instanceof THREE.WebGLRenderTarget ) {

                            _this.setTexture( texture.texture, textureUnit );

                        } else if ( texture instanceof THREE.WebGLRenderTargetCube ) {

                            setCubeTextureDynamic( texture.texture, textureUnit );

                        } else {

                            _this.setTexture( texture, textureUnit );

                        }

                    }

                    break;

                default:

                    console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type );

            }

        }

    }

    function setColorLinear( array, offset, color, intensity ) {

        array[ offset + 0 ] = color.r * intensity;
        array[ offset + 1 ] = color.g * intensity;
        array[ offset + 2 ] = color.b * intensity;

    }

    function setupLights ( lights, camera ) {

        var l, ll, light,
            r = 0, g = 0, b = 0,
            color, skyColor, groundColor,
            intensity,
            distance,

            zlights = _lights,

            viewMatrix = camera.matrixWorldInverse,

            dirColors = zlights.directional.colors,
            dirPositions = zlights.directional.positions,

            pointColors = zlights.point.colors,
            pointPositions = zlights.point.positions,
            pointDistances = zlights.point.distances,
            pointDecays = zlights.point.decays,

            spotColors = zlights.spot.colors,
            spotPositions = zlights.spot.positions,
            spotDistances = zlights.spot.distances,
            spotDirections = zlights.spot.directions,
            spotAnglesCos = zlights.spot.anglesCos,
            spotExponents = zlights.spot.exponents,
            spotDecays = zlights.spot.decays,

            hemiSkyColors = zlights.hemi.skyColors,
            hemiGroundColors = zlights.hemi.groundColors,
            hemiPositions = zlights.hemi.positions,

            dirLength = 0,
            pointLength = 0,
            spotLength = 0,
            hemiLength = 0,

            dirCount = 0,
            pointCount = 0,
            spotCount = 0,
            hemiCount = 0,

            dirOffset = 0,
            pointOffset = 0,
            spotOffset = 0,
            hemiOffset = 0;

        for ( l = 0, ll = lights.length; l < ll; l ++ ) {

            light = lights[ l ];

            color = light.color;
            intensity = light.intensity;
            distance = light.distance;

            if ( light instanceof THREE.AmbientLight ) {

                if ( ! light.visible ) continue;

                r += color.r;
                g += color.g;
                b += color.b;

            } else if ( light instanceof THREE.DirectionalLight ) {

                dirCount += 1;

                if ( ! light.visible ) continue;

                _direction.setFromMatrixPosition( light.matrixWorld );
                _vector3.setFromMatrixPosition( light.target.matrixWorld );
                _direction.sub( _vector3 );
                _direction.transformDirection( viewMatrix );

                dirOffset = dirLength * 3;

                dirPositions[ dirOffset + 0 ] = _direction.x;
                dirPositions[ dirOffset + 1 ] = _direction.y;
                dirPositions[ dirOffset + 2 ] = _direction.z;

                setColorLinear( dirColors, dirOffset, color, intensity );

                dirLength += 1;

            } else if ( light instanceof THREE.PointLight ) {

                pointCount += 1;

                if ( ! light.visible ) continue;

                pointOffset = pointLength * 3;

                setColorLinear( pointColors, pointOffset, color, intensity );

                _vector3.setFromMatrixPosition( light.matrixWorld );
                _vector3.applyMatrix4( viewMatrix );

                pointPositions[ pointOffset + 0 ] = _vector3.x;
                pointPositions[ pointOffset + 1 ] = _vector3.y;
                pointPositions[ pointOffset + 2 ] = _vector3.z;

                // distance is 0 if decay is 0, because there is no attenuation at all.
                pointDistances[ pointLength ] = distance;
                pointDecays[ pointLength ] = ( light.distance === 0 ) ? 0.0 : light.decay;

                pointLength += 1;

            } else if ( light instanceof THREE.SpotLight ) {

                spotCount += 1;

                if ( ! light.visible ) continue;

                spotOffset = spotLength * 3;

                setColorLinear( spotColors, spotOffset, color, intensity );

                _direction.setFromMatrixPosition( light.matrixWorld );
                _vector3.copy( _direction ).applyMatrix4( viewMatrix );

                spotPositions[ spotOffset + 0 ] = _vector3.x;
                spotPositions[ spotOffset + 1 ] = _vector3.y;
                spotPositions[ spotOffset + 2 ] = _vector3.z;

                spotDistances[ spotLength ] = distance;

                _vector3.setFromMatrixPosition( light.target.matrixWorld );
                _direction.sub( _vector3 );
                _direction.transformDirection( viewMatrix );

                spotDirections[ spotOffset + 0 ] = _direction.x;
                spotDirections[ spotOffset + 1 ] = _direction.y;
                spotDirections[ spotOffset + 2 ] = _direction.z;

                spotAnglesCos[ spotLength ] = Math.cos( light.angle );
                spotExponents[ spotLength ] = light.exponent;
                spotDecays[ spotLength ] = ( light.distance === 0 ) ? 0.0 : light.decay;

                spotLength += 1;

            } else if ( light instanceof THREE.HemisphereLight ) {

                hemiCount += 1;

                if ( ! light.visible ) continue;

                _direction.setFromMatrixPosition( light.matrixWorld );
                _direction.transformDirection( viewMatrix );

                hemiOffset = hemiLength * 3;

                hemiPositions[ hemiOffset + 0 ] = _direction.x;
                hemiPositions[ hemiOffset + 1 ] = _direction.y;
                hemiPositions[ hemiOffset + 2 ] = _direction.z;

                skyColor = light.color;
                groundColor = light.groundColor;

                setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity );
                setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity );

                hemiLength += 1;

            }

        }

        // null eventual remains from removed lights
        // (this is to avoid if in shader)

        for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0;
        for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0;
        for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0;
        for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0;
        for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0;

        zlights.directional.length = dirLength;
        zlights.point.length = pointLength;
        zlights.spot.length = spotLength;
        zlights.hemi.length = hemiLength;

        zlights.ambient[ 0 ] = r;
        zlights.ambient[ 1 ] = g;
        zlights.ambient[ 2 ] = b;

    }

    // GL state setting

    this.setFaceCulling = function ( cullFace, frontFaceDirection ) {

        if ( cullFace === THREE.CullFaceNone ) {

            state.disable( _gl.CULL_FACE );

        } else {

            if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) {

                _gl.frontFace( _gl.CW );

            } else {

                _gl.frontFace( _gl.CCW );

            }

            if ( cullFace === THREE.CullFaceBack ) {

                _gl.cullFace( _gl.BACK );

            } else if ( cullFace === THREE.CullFaceFront ) {

                _gl.cullFace( _gl.FRONT );

            } else {

                _gl.cullFace( _gl.FRONT_AND_BACK );

            }

            state.enable( _gl.CULL_FACE );

        }

    };

    // Textures

    function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) {

        var extension;

        if ( isImagePowerOfTwo ) {

            _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) );
            _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) );

            _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) );
            _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) );

        } else {

            _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
            _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );

            if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) {

                console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture );

            }

            _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );
            _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );

            if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) {

                console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture );

            }

        }

        extension = extensions.get( 'EXT_texture_filter_anisotropic' );

        if ( extension ) {

            if ( texture.type === THREE.FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return;
            if ( texture.type === THREE.HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return;

            if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) {

                _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) );
                properties.get( texture ).__currentAnisotropy = texture.anisotropy;

            }

        }

    }

    function uploadTexture( textureProperties, texture, slot ) {

        if ( textureProperties.__webglInit === undefined ) {

            textureProperties.__webglInit = true;

            texture.addEventListener( 'dispose', onTextureDispose );

            textureProperties.__webglTexture = _gl.createTexture();

            _infoMemory.textures ++;

        }

        state.activeTexture( _gl.TEXTURE0 + slot );
        state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );

        _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
        _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
        _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );

        texture.image = clampToMaxSize( texture.image, capabilities.maxTextureSize );

        if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false ) {

            texture.image = makePowerOfTwo( texture.image );

        }

        var image = texture.image,
            isImagePowerOfTwo = isPowerOfTwo( image ),
            glFormat = paramThreeToGL( texture.format ),
            glType = paramThreeToGL( texture.type );

        setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo );

        var mipmap, mipmaps = texture.mipmaps;

        if ( texture instanceof THREE.DataTexture ) {

            // use manually created mipmaps if available
            // if there are no manual mipmaps
            // set 0 level mipmap and then use GL to generate other mipmap levels

            if ( mipmaps.length > 0 && isImagePowerOfTwo ) {

                for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {

                    mipmap = mipmaps[ i ];
                    state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );

                }

                texture.generateMipmaps = false;

            } else {

                state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );

            }

        } else if ( texture instanceof THREE.CompressedTexture ) {

            for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {

                mipmap = mipmaps[ i ];

                if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) {

                    if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {

                        state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );

                    } else {

                        console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" );

                    }

                } else {

                    state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );

                }

            }

        } else {

            // regular Texture (image, video, canvas)

            // use manually created mipmaps if available
            // if there are no manual mipmaps
            // set 0 level mipmap and then use GL to generate other mipmap levels

            if ( mipmaps.length > 0 && isImagePowerOfTwo ) {

                for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {

                    mipmap = mipmaps[ i ];
                    state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap );

                }

                texture.generateMipmaps = false;

            } else {

                state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image );

            }

        }

        if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );

        textureProperties.__version = texture.version;

        if ( texture.onUpdate ) texture.onUpdate( texture );

    }

    this.setTexture = function ( texture, slot ) {

        var textureProperties = properties.get( texture );

        if ( texture.version > 0 && textureProperties.__version !== texture.version ) {

            var image = texture.image;

            if ( image === undefined ) {

                console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture );
                return;

            }

            if ( image.complete === false ) {

                console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture );
                return;

            }

            uploadTexture( textureProperties, texture, slot );

            return;

        }

        state.activeTexture( _gl.TEXTURE0 + slot );
        state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );

    };

    function clampToMaxSize ( image, maxSize ) {

        if ( image.width > maxSize || image.height > maxSize ) {

            // Warning: Scaling through the canvas will only work with images that use
            // premultiplied alpha.

            var scale = maxSize / Math.max( image.width, image.height );

            var canvas = document.createElement( 'canvas' );
            canvas.width = Math.floor( image.width * scale );
            canvas.height = Math.floor( image.height * scale );

            var context = canvas.getContext( '2d' );
            context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );

            console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );

            return canvas;

        }

        return image;

    }

    function isPowerOfTwo( image ) {

        return THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height );

    }

    function textureNeedsPowerOfTwo( texture ) {

        if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) return true;
        if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) return true;

        return false;

    }

    function makePowerOfTwo( image ) {

        if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement ) {

            var canvas = document.createElement( 'canvas' );
            canvas.width = THREE.Math.nearestPowerOfTwo( image.width );
            canvas.height = THREE.Math.nearestPowerOfTwo( image.height );

            var context = canvas.getContext( '2d' );
            context.drawImage( image, 0, 0, canvas.width, canvas.height );

            console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );

            return canvas;

        }

        return image;

    }

    function setCubeTexture ( texture, slot ) {

        var textureProperties = properties.get( texture );

        if ( texture.image.length === 6 ) {

            if ( texture.version > 0 && textureProperties.__version !== texture.version ) {

                if ( ! textureProperties.__image__webglTextureCube ) {

                    texture.addEventListener( 'dispose', onTextureDispose );

                    textureProperties.__image__webglTextureCube = _gl.createTexture();

                    _infoMemory.textures ++;

                }

                state.activeTexture( _gl.TEXTURE0 + slot );
                state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );

                _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );

                var isCompressed = texture instanceof THREE.CompressedTexture;
                var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture;

                var cubeImage = [];

                for ( var i = 0; i < 6; i ++ ) {

                    if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) {

                        cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize );

                    } else {

                        cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];

                    }

                }

                var image = cubeImage[ 0 ],
                    isImagePowerOfTwo = isPowerOfTwo( image ),
                    glFormat = paramThreeToGL( texture.format ),
                    glType = paramThreeToGL( texture.type );

                setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo );

                for ( var i = 0; i < 6; i ++ ) {

                    if ( ! isCompressed ) {

                        if ( isDataTexture ) {

                            state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );

                        } else {

                            state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );

                        }

                    } else {

                        var mipmap, mipmaps = cubeImage[ i ].mipmaps;

                        for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {

                            mipmap = mipmaps[ j ];

                            if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) {

                                if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {

                                    state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );

                                } else {

                                    console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()" );

                                }

                            } else {

                                state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );

                            }

                        }

                    }

                }

                if ( texture.generateMipmaps && isImagePowerOfTwo ) {

                    _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );

                }

                textureProperties.__version = texture.version;

                if ( texture.onUpdate ) texture.onUpdate( texture );

            } else {

                state.activeTexture( _gl.TEXTURE0 + slot );
                state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );

            }

        }

    }

    function setCubeTextureDynamic ( texture, slot ) {

        state.activeTexture( _gl.TEXTURE0 + slot );
        state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture );

    }

    // Render targets

    function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) {

        _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
        _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 );

    }

    function setupRenderBuffer ( renderbuffer, renderTarget ) {

        _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );

        if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {

            _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height );
            _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );

            /* For some reason this is not working. Defaulting to RGBA4.
		} else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) {

			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height );
			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
		*/

        } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {

            _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );
            _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );

        } else {

            _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height );

        }

    }

    this.setRenderTarget = function ( renderTarget ) {

        var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube );

        if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) {

            var renderTargetProperties = properties.get( renderTarget );
            var textureProperties = properties.get( renderTarget.texture );

            if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true;
            if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true;

            renderTarget.addEventListener( 'dispose', onRenderTargetDispose );

            textureProperties.__webglTexture = _gl.createTexture();

            _infoMemory.textures ++;

            // Setup texture, create render and frame buffers

            var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ),
                glFormat = paramThreeToGL( renderTarget.texture.format ),
                glType = paramThreeToGL( renderTarget.texture.type );

            if ( isCube ) {

                renderTargetProperties.__webglFramebuffer = [];
                renderTargetProperties.__webglRenderbuffer = [];

                state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );

                setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo );

                for ( var i = 0; i < 6; i ++ ) {

                    renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer();
                    renderTargetProperties.__webglRenderbuffer[ i ] = _gl.createRenderbuffer();
                    state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );

                    setupFrameBuffer( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
                    setupRenderBuffer( renderTargetProperties.__webglRenderbuffer[ i ], renderTarget );

                }

                if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );

            } else {

                renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer();

                if ( renderTarget.shareDepthFrom ) {

                    renderTargetProperties.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer;

                } else {

                    renderTargetProperties.__webglRenderbuffer = _gl.createRenderbuffer();

                }

                state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );
                setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo );

                state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );

                setupFrameBuffer( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D );

                if ( renderTarget.shareDepthFrom ) {

                    if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {

                        _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer );

                    } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {

                        _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer );

                    }

                } else {

                    setupRenderBuffer( renderTargetProperties.__webglRenderbuffer, renderTarget );

                }

                if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );

            }

            // Release everything

            if ( isCube ) {

                state.bindTexture( _gl.TEXTURE_CUBE_MAP, null );

            } else {

                state.bindTexture( _gl.TEXTURE_2D, null );

            }

            _gl.bindRenderbuffer( _gl.RENDERBUFFER, null );
            _gl.bindFramebuffer( _gl.FRAMEBUFFER, null );

        }

        var framebuffer, width, height, vx, vy;

        if ( renderTarget ) {

            var renderTargetProperties = properties.get( renderTarget );

            if ( isCube ) {

                framebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ];

            } else {

                framebuffer = renderTargetProperties.__webglFramebuffer;

            }

            width = renderTarget.width;
            height = renderTarget.height;

            vx = 0;
            vy = 0;

        } else {

            framebuffer = null;

            width = _viewportWidth;
            height = _viewportHeight;

            vx = _viewportX;
            vy = _viewportY;

        }

        if ( framebuffer !== _currentFramebuffer ) {

            _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
            _gl.viewport( vx, vy, width, height );

            _currentFramebuffer = framebuffer;

        }

        if ( isCube ) {

            var textureProperties = properties.get( renderTarget.texture );
            _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, 0 );

        }

        _currentWidth = width;
        _currentHeight = height;

    };

    this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) {

        if ( renderTarget instanceof THREE.WebGLRenderTarget === false ) {

            console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );
            return;

        }

        var framebuffer = properties.get( renderTarget ).__webglFramebuffer;

        if ( framebuffer ) {

            var restore = false;

            if ( framebuffer !== _currentFramebuffer ) {

                _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );

                restore = true;

            }

            try {

                var texture = renderTarget.texture;

                if ( texture.format !== THREE.RGBAFormat
                    && paramThreeToGL( texture.format ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) {

                    console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' );
                    return;

                }

                if ( texture.type !== THREE.UnsignedByteType
                    && paramThreeToGL( texture.type ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE )
                    && ! ( texture.type === THREE.FloatType && extensions.get( 'WEBGL_color_buffer_float' ) )
                    && ! ( texture.type === THREE.HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) {

                    console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' );
                    return;

                }

                if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) {

                    _gl.readPixels( x, y, width, height, paramThreeToGL( texture.format ), paramThreeToGL( texture.type ), buffer );

                } else {

                    console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' );

                }

            } finally {

                if ( restore ) {

                    _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer );

                }

            }

        }

    };

    function updateRenderTargetMipmap( renderTarget ) {

        var target = renderTarget instanceof THREE.WebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D;
        var texture = properties.get( renderTarget.texture ).__webglTexture;

        state.bindTexture( target, texture );
        _gl.generateMipmap( target );
        state.bindTexture( target, null );

    }

    // Fallback filters for non-power-of-2 textures

    function filterFallback ( f ) {

        if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) {

            return _gl.NEAREST;

        }

        return _gl.LINEAR;

    }

    // Map three.js constants to WebGL constants

    function paramThreeToGL ( p ) {

        var extension;

        if ( p === THREE.RepeatWrapping ) return _gl.REPEAT;
        if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE;
        if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT;

        if ( p === THREE.NearestFilter ) return _gl.NEAREST;
        if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST;
        if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR;

        if ( p === THREE.LinearFilter ) return _gl.LINEAR;
        if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST;
        if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR;

        if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE;
        if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4;
        if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1;
        if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5;

        if ( p === THREE.ByteType ) return _gl.BYTE;
        if ( p === THREE.ShortType ) return _gl.SHORT;
        if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT;
        if ( p === THREE.IntType ) return _gl.INT;
        if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT;
        if ( p === THREE.FloatType ) return _gl.FLOAT;

        extension = extensions.get( 'OES_texture_half_float' );

        if ( extension !== null ) {

            if ( p === THREE.HalfFloatType ) return extension.HALF_FLOAT_OES;

        }

        if ( p === THREE.AlphaFormat ) return _gl.ALPHA;
        if ( p === THREE.RGBFormat ) return _gl.RGB;
        if ( p === THREE.RGBAFormat ) return _gl.RGBA;
        if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE;
        if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA;

        if ( p === THREE.AddEquation ) return _gl.FUNC_ADD;
        if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT;
        if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT;

        if ( p === THREE.ZeroFactor ) return _gl.ZERO;
        if ( p === THREE.OneFactor ) return _gl.ONE;
        if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR;
        if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR;
        if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA;
        if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA;
        if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA;
        if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA;

        if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR;
        if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR;
        if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE;

        extension = extensions.get( 'WEBGL_compressed_texture_s3tc' );

        if ( extension !== null ) {

            if ( p === THREE.RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
            if ( p === THREE.RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
            if ( p === THREE.RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
            if ( p === THREE.RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;

        }

        extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );

        if ( extension !== null ) {

            if ( p === THREE.RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
            if ( p === THREE.RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
            if ( p === THREE.RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
            if ( p === THREE.RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;

        }

        extension = extensions.get( 'EXT_blend_minmax' );

        if ( extension !== null ) {

            if ( p === THREE.MinEquation ) return extension.MIN_EXT;
            if ( p === THREE.MaxEquation ) return extension.MAX_EXT;

        }

        return 0;

    }

    // DEPRECATED

    this.supportsFloatTextures = function () {

        console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' );
        return extensions.get( 'OES_texture_float' );

    };

    this.supportsHalfFloatTextures = function () {

        console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' );
        return extensions.get( 'OES_texture_half_float' );

    };

    this.supportsStandardDerivatives = function () {

        console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' );
        return extensions.get( 'OES_standard_derivatives' );

    };

    this.supportsCompressedTextureS3TC = function () {

        console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' );
        return extensions.get( 'WEBGL_compressed_texture_s3tc' );

    };

    this.supportsCompressedTexturePVRTC = function () {

        console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' );
        return extensions.get( 'WEBGL_compressed_texture_pvrtc' );

    };

    this.supportsBlendMinMax = function () {

        console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' );
        return extensions.get( 'EXT_blend_minmax' );

    };

    this.supportsVertexTextures = function () {

        return capabilities.vertexTextures;

    };

    this.supportsInstancedArrays = function () {

        console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' );
        return extensions.get( 'ANGLE_instanced_arrays' );

    };

    //

    this.initMaterial = function () {

        console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );

    };

    this.addPrePlugin = function () {

        console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' );

    };

    this.addPostPlugin = function () {

        console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' );

    };

    this.updateShadowMap = function () {

        console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' );

    };

    Object.defineProperties( this, {
        shadowMapEnabled: {
            get: function () {

                return shadowMap.enabled;

            },
            set: function ( value ) {

                console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' );
                shadowMap.enabled = value;

            }
        },
        shadowMapType: {
            get: function () {

                return shadowMap.type;

            },
            set: function ( value ) {

                console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' );
                shadowMap.type = value;

            }
        },
        shadowMapCullFace: {
            get: function () {

                return shadowMap.cullFace;

            },
            set: function ( value ) {

                console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.' );
                shadowMap.cullFace = value;

            }
        },
        shadowMapDebug: {
            get: function () {

                return shadowMap.debug;

            },
            set: function ( value ) {

                console.warn( 'THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug.' );
                shadowMap.debug = value;

            }
        }
    } );

};

// File:src/renderers/WebGLRenderTarget.js

/**
 * @author szimek / https://github.com/szimek/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.WebGLRenderTarget = function ( width, height, options ) {

    this.uuid = THREE.Math.generateUUID();

    this.width = width;
    this.height = height;

    options = options || {};

    if ( options.minFilter === undefined ) options.minFilter = THREE.LinearFilter;

    this.texture = new THREE.Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy );

    this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
    this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;

    this.shareDepthFrom = options.shareDepthFrom !== undefined ? options.shareDepthFrom : null;

};

THREE.WebGLRenderTarget.prototype = {

    constructor: THREE.WebGLRenderTarget,

    get wrapS() {

        console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );

        return this.texture.wrapS;

    },

    set wrapS( value ) {

        console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );

        this.texture.wrapS = value;

    },

    get wrapT() {

        console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );

        return this.texture.wrapT;

    },

    set wrapT( value ) {

        console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );

        this.texture.wrapT = value;

    },

    get magFilter() {

        console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );

        return this.texture.magFilter;

    },

    set magFilter( value ) {

        console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );

        this.texture.magFilter = value;

    },

    get minFilter() {

        console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );

        return this.texture.minFilter;

    },

    set minFilter( value ) {

        console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );

        this.texture.minFilter = value;

    },

    get anisotropy() {

        console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );

        return this.texture.anisotropy;

    },

    set anisotropy( value ) {

        console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );

        this.texture.anisotropy = value;

    },

    get offset() {

        console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );

        return this.texture.offset;

    },

    set offset( value ) {

        console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );

        this.texture.offset = value;

    },

    get repeat() {

        console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );

        return this.texture.repeat;

    },

    set repeat( value ) {

        console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );

        this.texture.repeat = value;

    },

    get format() {

        console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );

        return this.texture.format;

    },

    set format( value ) {

        console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );

        this.texture.format = value;

    },

    get type() {

        console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );

        return this.texture.type;

    },

    set type( value ) {

        console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );

        this.texture.type = value;

    },

    get generateMipmaps() {

        console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );

        return this.texture.generateMipmaps;

    },

    set generateMipmaps( value ) {

        console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );

        this.texture.generateMipmaps = value;

    },

    //

    setSize: function ( width, height ) {

        if ( this.width !== width || this.height !== height ) {

            this.width = width;
            this.height = height;

            this.dispose();

        }

    },

    clone: function () {

        return new this.constructor().copy( this );

    },

    copy: function ( source ) {

        this.width = source.width;
        this.height = source.height;

        this.texture = source.texture.clone();

        this.depthBuffer = source.depthBuffer;
        this.stencilBuffer = source.stencilBuffer;

        this.shareDepthFrom = source.shareDepthFrom;

        return this;

    },

    dispose: function () {

        this.dispatchEvent( { type: 'dispose' } );

    }

};

THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype );

// File:src/renderers/WebGLRenderTargetCube.js

/**
 * @author alteredq / http://alteredqualia.com
 */

THREE.WebGLRenderTargetCube = function ( width, height, options ) {

    THREE.WebGLRenderTarget.call( this, width, height, options );

    this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5

};

THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype );
THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube;

// File:src/renderers/webgl/WebGLBufferRenderer.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.WebGLBufferRenderer = function ( _gl, extensions, _infoRender ) {

    var mode;

    function setMode( value ) {

        mode = value;

    }

    function render( start, count ) {

        _gl.drawArrays( mode, start, count );

        _infoRender.calls ++;
        _infoRender.vertices += count;
        if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3;

    }

    function renderInstances( geometry ) {

        var extension = extensions.get( 'ANGLE_instanced_arrays' );

        if ( extension === null ) {

            console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
            return;

        }

        var position = geometry.attributes.position;

        if ( position instanceof THREE.InterleavedBufferAttribute ) {

            extension.drawArraysInstancedANGLE( mode, 0, position.data.count, geometry.maxInstancedCount );

        } else {

            extension.drawArraysInstancedANGLE( mode, 0, position.count, geometry.maxInstancedCount );

        }

    }

    this.setMode = setMode;
    this.render = render;
    this.renderInstances = renderInstances;

};

// File:src/renderers/webgl/WebGLIndexedBufferRenderer.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.WebGLIndexedBufferRenderer = function ( _gl, extensions, _infoRender ) {

    var mode;

    function setMode( value ) {

        mode = value;

    }

    var type, size;

    function setIndex( index ) {

        if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) {

            type = _gl.UNSIGNED_INT;
            size = 4;

        } else {

            type = _gl.UNSIGNED_SHORT;
            size = 2;

        }

    }

    function render( start, count ) {

        _gl.drawElements( mode, count, type, start * size );

        _infoRender.calls ++;
        _infoRender.vertices += count;
        if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3;

    }

    function renderInstances( geometry ) {

        var extension = extensions.get( 'ANGLE_instanced_arrays' );

        if ( extension === null ) {

            console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
            return;

        }

        var index = geometry.index;

        extension.drawElementsInstancedANGLE( mode, index.array.length, type, 0, geometry.maxInstancedCount );

    }

    this.setMode = setMode;
    this.setIndex = setIndex;
    this.render = render;
    this.renderInstances = renderInstances;

};

// File:src/renderers/webgl/WebGLExtensions.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.WebGLExtensions = function ( gl ) {

    var extensions = {};

    this.get = function ( name ) {

        if ( extensions[ name ] !== undefined ) {

            return extensions[ name ];

        }

        var extension;

        switch ( name ) {

            case 'EXT_texture_filter_anisotropic':
                extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
                break;

            case 'WEBGL_compressed_texture_s3tc':
                extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
                break;

            case 'WEBGL_compressed_texture_pvrtc':
                extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );
                break;

            default:
                extension = gl.getExtension( name );

        }

        if ( extension === null ) {

            console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );

        }

        extensions[ name ] = extension;

        return extension;

    };

};

// File:src/renderers/webgl/WebGLCapabilities.js

THREE.WebGLCapabilities = function ( gl, extensions, parameters ) {

    function getMaxPrecision( precision ) {

        if ( precision === 'highp' ) {

            if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 &&
                gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) {

                return 'highp';

            }

            precision = 'mediump';

        }

        if ( precision === 'mediump' ) {

            if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 &&
                gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) {

                return 'mediump';

            }

        }

        return 'lowp';

    }

    this.getMaxPrecision = getMaxPrecision;

    this.precision = parameters.precision !== undefined ? parameters.precision : 'highp',
        this.logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false;

    this.maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );
    this.maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
    this.maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE );
    this.maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE );

    this.maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );
    this.maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS );
    this.maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS );
    this.maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS );

    this.vertexTextures = this.maxVertexTextures > 0;
    this.floatFragmentTextures = !! extensions.get( 'OES_texture_float' );
    this.floatVertexTextures = this.vertexTextures && this.floatFragmentTextures;

    var _maxPrecision = getMaxPrecision( this.precision );

    if ( _maxPrecision !== this.precision ) {

        console.warn( 'THREE.WebGLRenderer:', this.precision, 'not supported, using', _maxPrecision, 'instead.' );
        this.precision = _maxPrecision;

    }

    if ( this.logarithmicDepthBuffer ) {

        this.logarithmicDepthBuffer = !! extensions.get( 'EXT_frag_depth' );

    }

};

// File:src/renderers/webgl/WebGLGeometries.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.WebGLGeometries = function ( gl, properties, info ) {

    var geometries = {};

    function get( object ) {

        var geometry = object.geometry;

        if ( geometries[ geometry.id ] !== undefined ) {

            return geometries[ geometry.id ];

        }

        geometry.addEventListener( 'dispose', onGeometryDispose );

        var buffergeometry;

        if ( geometry instanceof THREE.BufferGeometry ) {

            buffergeometry = geometry;

        } else if ( geometry instanceof THREE.Geometry ) {

            if ( geometry._bufferGeometry === undefined ) {

                geometry._bufferGeometry = new THREE.BufferGeometry().setFromObject( object );

            }

            buffergeometry = geometry._bufferGeometry;

        }

        geometries[ geometry.id ] = buffergeometry;

        info.memory.geometries ++;

        return buffergeometry;

    }

    function onGeometryDispose( event ) {

        var geometry = event.target;
        var buffergeometry = geometries[ geometry.id ];

        deleteAttributes( buffergeometry.attributes );

        geometry.removeEventListener( 'dispose', onGeometryDispose );

        delete geometries[ geometry.id ];

        var property = properties.get( geometry );
        if ( property.wireframe ) deleteAttribute( property.wireframe );

        info.memory.geometries --;

    }

    function getAttributeBuffer( attribute ) {

        if ( attribute instanceof THREE.InterleavedBufferAttribute ) {

            return properties.get( attribute.data ).__webglBuffer;

        }

        return properties.get( attribute ).__webglBuffer;

    }

    function deleteAttribute( attribute ) {

        var buffer = getAttributeBuffer( attribute );

        if ( buffer !== undefined ) {

            gl.deleteBuffer( buffer );
            removeAttributeBuffer( attribute );

        }

    }

    function deleteAttributes( attributes ) {

        for ( var name in attributes ) {

            deleteAttribute( attributes[ name ] );

        }

    }

    function removeAttributeBuffer( attribute ) {

        if ( attribute instanceof THREE.InterleavedBufferAttribute ) {

            properties.delete( attribute.data );

        } else {

            properties.delete( attribute );

        }

    }

    this.get = get;

};

// File:src/renderers/webgl/WebGLObjects.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.WebGLObjects = function ( gl, properties, info ) {

    var geometries = new THREE.WebGLGeometries( gl, properties, info );

    //

    function update( object ) {

        // TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter.

        var geometry = geometries.get( object );

        if ( object.geometry instanceof THREE.Geometry ) {

            geometry.updateFromObject( object );

        }

        var index = geometry.index;
        var attributes = geometry.attributes;

        if ( index !== null ) {

            updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER );

        }

        for ( var name in attributes ) {

            updateAttribute( attributes[ name ], gl.ARRAY_BUFFER );

        }

        // morph targets

        var morphAttributes = geometry.morphAttributes;

        for ( var name in morphAttributes ) {

            var array = morphAttributes[ name ];

            for ( var i = 0, l = array.length; i < l; i ++ ) {

                updateAttribute( array[ i ], gl.ARRAY_BUFFER );

            }

        }

        return geometry;

    }

    function updateAttribute( attribute, bufferType ) {

        var data = ( attribute instanceof THREE.InterleavedBufferAttribute ) ? attribute.data : attribute;

        var attributeProperties = properties.get( data );

        if ( attributeProperties.__webglBuffer === undefined ) {

            createBuffer( attributeProperties, data, bufferType );

        } else if ( attributeProperties.version !== data.version ) {

            updateBuffer( attributeProperties, data, bufferType );

        }

    }

    function createBuffer( attributeProperties, data, bufferType ) {

        attributeProperties.__webglBuffer = gl.createBuffer();
        gl.bindBuffer( bufferType, attributeProperties.__webglBuffer );

        var usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW;

        gl.bufferData( bufferType, data.array, usage );

        attributeProperties.version = data.version;

    }

    function updateBuffer( attributeProperties, data, bufferType ) {

        gl.bindBuffer( bufferType, attributeProperties.__webglBuffer );

        if ( data.dynamic === false || data.updateRange.count === - 1 ) {

            // Not using update ranges

            gl.bufferSubData( bufferType, 0, data.array );

        } else if ( data.updateRange.count === 0 ) {

            console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' );

        } else {

            gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT,
                data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) );

            data.updateRange.count = 0; // reset range

        }

        attributeProperties.version = data.version;

    }

    function getAttributeBuffer( attribute ) {

        if ( attribute instanceof THREE.InterleavedBufferAttribute ) {

            return properties.get( attribute.data ).__webglBuffer;

        }

        return properties.get( attribute ).__webglBuffer;

    }

    function getWireframeAttribute( geometry ) {

        var property = properties.get( geometry );

        if ( property.wireframe !== undefined ) {

            return property.wireframe;

        }

        var indices = [];

        var index = geometry.index;
        var attributes = geometry.attributes;
        var position = attributes.position;

        // console.time( 'wireframe' );

        if ( index !== null ) {

            var edges = {};
            var array = index.array;

            for ( var i = 0, l = array.length; i < l; i += 3 ) {

                var a = array[ i + 0 ];
                var b = array[ i + 1 ];
                var c = array[ i + 2 ];

                if ( checkEdge( edges, a, b ) ) indices.push( a, b );
                if ( checkEdge( edges, b, c ) ) indices.push( b, c );
                if ( checkEdge( edges, c, a ) ) indices.push( c, a );

            }

        } else {

            var array = attributes.position.array;

            for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {

                var a = i + 0;
                var b = i + 1;
                var c = i + 2;

                indices.push( a, b, b, c, c, a );

            }

        }

        // console.timeEnd( 'wireframe' );

        var TypeArray = position.count > 65535 ? Uint32Array : Uint16Array;
        var attribute = new THREE.BufferAttribute( new TypeArray( indices ), 1 );

        updateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER );

        property.wireframe = attribute;

        return attribute;

    }

    function checkEdge( edges, a, b ) {

        if ( a > b ) {

            var tmp = a;
            a = b;
            b = tmp;

        }

        var list = edges[ a ];

        if ( list === undefined ) {

            edges[ a ] = [ b ];
            return true;

        } else if ( list.indexOf( b ) === -1 ) {

            list.push( b );
            return true;

        }

        return false;

    }

    this.getAttributeBuffer = getAttributeBuffer;
    this.getWireframeAttribute = getWireframeAttribute;

    this.update = update;

};

// File:src/renderers/webgl/WebGLProgram.js

THREE.WebGLProgram = ( function () {

    var programIdCount = 0;

    function generateDefines( defines ) {

        var chunks = [];

        for ( var name in defines ) {

            var value = defines[ name ];

            if ( value === false ) continue;

            chunks.push( '#define ' + name + ' ' + value );

        }

        return chunks.join( '\n' );

    }

    function fetchUniformLocations( gl, program, identifiers ) {

        var uniforms = {};

        var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );

        for ( var i = 0; i < n; i ++ ) {

            var info = gl.getActiveUniform( program, i );
            var name = info.name;
            var location = gl.getUniformLocation( program, name );

            // console.log("THREE.WebGLProgram: ACTIVE UNIFORM:", name);

            var suffixPos = name.lastIndexOf( '[0]' );
            if ( suffixPos !== - 1 && suffixPos === name.length - 3 ) {

                uniforms[ name.substr( 0, suffixPos ) ] = location;

            }

            uniforms[ name ] = location;

        }

        return uniforms;

    }

    function fetchAttributeLocations( gl, program, identifiers ) {

        var attributes = {};

        var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );

        for ( var i = 0; i < n; i ++ ) {

            var info = gl.getActiveAttrib( program, i );
            var name = info.name;

            // console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i );

            attributes[ name ] = gl.getAttribLocation( program, name );

        }

        return attributes;

    }

    function filterEmptyLine( string ) {

        return string !== '';

    }

    return function WebGLProgram( renderer, code, material, parameters ) {

        var gl = renderer.context;

        var defines = material.defines;

        var vertexShader = material.__webglShader.vertexShader;
        var fragmentShader = material.__webglShader.fragmentShader;

        var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';

        if ( parameters.shadowMapType === THREE.PCFShadowMap ) {

            shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';

        } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) {

            shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';

        }

        var envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
        var envMapModeDefine = 'ENVMAP_MODE_REFLECTION';
        var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';

        if ( parameters.envMap ) {

            switch ( material.envMap.mapping ) {

                case THREE.CubeReflectionMapping:
                case THREE.CubeRefractionMapping:
                    envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
                    break;

                case THREE.EquirectangularReflectionMapping:
                case THREE.EquirectangularRefractionMapping:
                    envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC';
                    break;

                case THREE.SphericalReflectionMapping:
                    envMapTypeDefine = 'ENVMAP_TYPE_SPHERE';
                    break;

            }

            switch ( material.envMap.mapping ) {

                case THREE.CubeRefractionMapping:
                case THREE.EquirectangularRefractionMapping:
                    envMapModeDefine = 'ENVMAP_MODE_REFRACTION';
                    break;

            }

            switch ( material.combine ) {

                case THREE.MultiplyOperation:
                    envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
                    break;

                case THREE.MixOperation:
                    envMapBlendingDefine = 'ENVMAP_BLENDING_MIX';
                    break;

                case THREE.AddOperation:
                    envMapBlendingDefine = 'ENVMAP_BLENDING_ADD';
                    break;

            }

        }

        var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0;

        // console.log( 'building new program ' );

        //

        var customDefines = generateDefines( defines );

        //

        var program = gl.createProgram();

        var prefixVertex, prefixFragment;

        if ( material instanceof THREE.RawShaderMaterial ) {

            prefixVertex = '';
            prefixFragment = '';

        } else {

            prefixVertex = [

                'precision ' + parameters.precision + ' float;',
                'precision ' + parameters.precision + ' int;',

                '#define SHADER_NAME ' + material.__webglShader.name,

                customDefines,

                parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',

                renderer.gammaInput ? '#define GAMMA_INPUT' : '',
                renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '',
                '#define GAMMA_FACTOR ' + gammaFactorDefine,

                '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
                '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
                '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
                '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,

                '#define MAX_SHADOWS ' + parameters.maxShadows,

                '#define MAX_BONES ' + parameters.maxBones,

                parameters.map ? '#define USE_MAP' : '',
                parameters.envMap ? '#define USE_ENVMAP' : '',
                parameters.envMap ? '#define ' + envMapModeDefine : '',
                parameters.lightMap ? '#define USE_LIGHTMAP' : '',
                parameters.aoMap ? '#define USE_AOMAP' : '',
                parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
                parameters.bumpMap ? '#define USE_BUMPMAP' : '',
                parameters.normalMap ? '#define USE_NORMALMAP' : '',
                parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',
                parameters.specularMap ? '#define USE_SPECULARMAP' : '',
                parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
                parameters.vertexColors ? '#define USE_COLOR' : '',

                parameters.flatShading ? '#define FLAT_SHADED' : '',

                parameters.skinning ? '#define USE_SKINNING' : '',
                parameters.useVertexTexture ? '#define BONE_TEXTURE' : '',

                parameters.morphTargets ? '#define USE_MORPHTARGETS' : '',
                parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '',
                parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
                parameters.flipSided ? '#define FLIP_SIDED' : '',

                parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
                parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
                parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
                parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '',

                parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',

                parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
                parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',


                'uniform mat4 modelMatrix;',
                'uniform mat4 modelViewMatrix;',
                'uniform mat4 projectionMatrix;',
                'uniform mat4 viewMatrix;',
                'uniform mat3 normalMatrix;',
                'uniform vec3 cameraPosition;',

                'attribute vec3 position;',
                'attribute vec3 normal;',
                'attribute vec2 uv;',

                '#ifdef USE_COLOR',

                '	attribute vec3 color;',

                '#endif',

                '#ifdef USE_MORPHTARGETS',

                '	attribute vec3 morphTarget0;',
                '	attribute vec3 morphTarget1;',
                '	attribute vec3 morphTarget2;',
                '	attribute vec3 morphTarget3;',

                '	#ifdef USE_MORPHNORMALS',

                '		attribute vec3 morphNormal0;',
                '		attribute vec3 morphNormal1;',
                '		attribute vec3 morphNormal2;',
                '		attribute vec3 morphNormal3;',

                '	#else',

                '		attribute vec3 morphTarget4;',
                '		attribute vec3 morphTarget5;',
                '		attribute vec3 morphTarget6;',
                '		attribute vec3 morphTarget7;',

                '	#endif',

                '#endif',

                '#ifdef USE_SKINNING',

                '	attribute vec4 skinIndex;',
                '	attribute vec4 skinWeight;',

                '#endif',

                '\n'

            ].filter( filterEmptyLine ).join( '\n' );

            prefixFragment = [

                parameters.bumpMap || parameters.normalMap || parameters.flatShading || material.derivatives ? '#extension GL_OES_standard_derivatives : enable' : '',
                parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',

                'precision ' + parameters.precision + ' float;',
                'precision ' + parameters.precision + ' int;',

                '#define SHADER_NAME ' + material.__webglShader.name,

                customDefines,

                '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
                '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
                '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
                '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,

                '#define MAX_SHADOWS ' + parameters.maxShadows,

                parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '',

                renderer.gammaInput ? '#define GAMMA_INPUT' : '',
                renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '',
                '#define GAMMA_FACTOR ' + gammaFactorDefine,

                ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',
                ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',

                parameters.map ? '#define USE_MAP' : '',
                parameters.envMap ? '#define USE_ENVMAP' : '',
                parameters.envMap ? '#define ' + envMapTypeDefine : '',
                parameters.envMap ? '#define ' + envMapModeDefine : '',
                parameters.envMap ? '#define ' + envMapBlendingDefine : '',
                parameters.lightMap ? '#define USE_LIGHTMAP' : '',
                parameters.aoMap ? '#define USE_AOMAP' : '',
                parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
                parameters.bumpMap ? '#define USE_BUMPMAP' : '',
                parameters.normalMap ? '#define USE_NORMALMAP' : '',
                parameters.specularMap ? '#define USE_SPECULARMAP' : '',
                parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
                parameters.vertexColors ? '#define USE_COLOR' : '',

                parameters.flatShading ? '#define FLAT_SHADED' : '',

                parameters.metal ? '#define METAL' : '',
                parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
                parameters.flipSided ? '#define FLIP_SIDED' : '',

                parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
                parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
                parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
                parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '',

                parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
                parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',

                'uniform mat4 viewMatrix;',
                'uniform vec3 cameraPosition;',

                '\n'

            ].filter( filterEmptyLine ).join( '\n' );

        }

        var vertexGlsl = prefixVertex + vertexShader;
        var fragmentGlsl = prefixFragment + fragmentShader;

        var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );
        var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );

        gl.attachShader( program, glVertexShader );
        gl.attachShader( program, glFragmentShader );

        // Force a particular attribute to index 0.

        if ( material.index0AttributeName !== undefined ) {

            gl.bindAttribLocation( program, 0, material.index0AttributeName );

        } else if ( parameters.morphTargets === true ) {

            // programs with morphTargets displace position out of attribute 0
            gl.bindAttribLocation( program, 0, 'position' );

        }

        gl.linkProgram( program );

        var programLog = gl.getProgramInfoLog( program );
        var vertexLog = gl.getShaderInfoLog( glVertexShader );
        var fragmentLog = gl.getShaderInfoLog( glFragmentShader );

        var runnable = true;
        var haveDiagnostics = true;

        if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {

            runnable = false;

            console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog );

        } else if ( programLog !== '' ) {

            console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog );

        } else if ( vertexLog === '' || fragmentLog === '' ) {

            haveDiagnostics = false;

        }

        if ( haveDiagnostics ) {

            this.diagnostics = {

                runnable: runnable,
                material: material,

                programLog: programLog,

                vertexShader: {

                    log: vertexLog,
                    prefix: prefixVertex

                },

                fragmentShader: {

                    log: fragmentLog,
                    prefix: prefixFragment

                }

            };

        }

        // clean up

        gl.deleteShader( glVertexShader );
        gl.deleteShader( glFragmentShader );

        // set up caching for uniform locations

        var cachedUniforms;

        this.getUniforms = function() {

            if ( cachedUniforms === undefined ) {

                cachedUniforms = fetchUniformLocations( gl, program );

            }

            return cachedUniforms;

        };

        // set up caching for attribute locations

        var cachedAttributes;

        this.getAttributes = function() {

            if ( cachedAttributes === undefined ) {

                cachedAttributes = fetchAttributeLocations( gl, program );

            }

            return cachedAttributes;

        };

        // free resource

        this.destroy = function() {

            gl.deleteProgram( program );
            this.program = undefined;

        };

        // DEPRECATED

        Object.defineProperties( this, {

            uniforms: {
                get: function() {

                    console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' );
                    return this.getUniforms();

                }
            },

            attributes: {
                get: function() {

                    console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' );
                    return this.getAttributes();

                }
            }

        } );


        //

        this.id = programIdCount ++;
        this.code = code;
        this.usedTimes = 1;
        this.program = program;
        this.vertexShader = glVertexShader;
        this.fragmentShader = glFragmentShader;

        return this;

    };

} )();

// File:src/renderers/webgl/WebGLPrograms.js

THREE.WebGLPrograms = function ( renderer, capabilities ) {

    var programs = [];

    var shaderIDs = {
        MeshDepthMaterial: 'depth',
        MeshNormalMaterial: 'normal',
        MeshBasicMaterial: 'basic',
        MeshLambertMaterial: 'lambert',
        MeshPhongMaterial: 'phong',
        LineBasicMaterial: 'basic',
        LineDashedMaterial: 'dashed',
        PointsMaterial: 'points'
    };

    var parameterNames = [
        "precision", "supportsVertexTextures", "map", "envMap", "envMapMode",
        "lightMap", "aoMap", "emissiveMap", "bumpMap", "normalMap", "displacementMap", "specularMap",
        "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp",
        "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
        "maxBones", "useVertexTexture", "morphTargets", "morphNormals",
        "maxMorphTargets", "maxMorphNormals", "maxDirLights", "maxPointLights",
        "maxSpotLights", "maxHemiLights", "maxShadows", "shadowMapEnabled", "pointLightShadows",
        "shadowMapType", "shadowMapDebug", "alphaTest", "metal", "doubleSided",
        "flipSided"
    ];


    function allocateBones ( object ) {

        if ( capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture ) {

            return 1024;

        } else {

            // default for when object is not specified
            // ( for example when prebuilding shader to be used with multiple objects )
            //
            //  - leave some extra space for other uniforms
            //  - limit here is ANGLE's 254 max uniform vectors
            //    (up to 54 should be safe)

            var nVertexUniforms = capabilities.maxVertexUniforms;
            var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );

            var maxBones = nVertexMatrices;

            if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {

                maxBones = Math.min( object.skeleton.bones.length, maxBones );

                if ( maxBones < object.skeleton.bones.length ) {

                    console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' );

                }

            }

            return maxBones;

        }

    }

    function allocateLights( lights ) {

        var dirLights = 0;
        var pointLights = 0;
        var spotLights = 0;
        var hemiLights = 0;

        for ( var l = 0, ll = lights.length; l < ll; l ++ ) {

            var light = lights[ l ];

            if ( light.visible === false ) continue;

            if ( light instanceof THREE.DirectionalLight ) dirLights ++;
            if ( light instanceof THREE.PointLight ) pointLights ++;
            if ( light instanceof THREE.SpotLight ) spotLights ++;
            if ( light instanceof THREE.HemisphereLight ) hemiLights ++;

        }

        return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights };

    }

    function allocateShadows( lights ) {

        var maxShadows = 0;
        var pointLightShadows = 0;

        for ( var l = 0, ll = lights.length; l < ll; l ++ ) {

            var light = lights[ l ];

            if ( ! light.castShadow ) continue;

            if ( light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) maxShadows ++;
            if ( light instanceof THREE.PointLight ) {

                maxShadows ++;
                pointLightShadows ++;

            }

        }

        return { 'maxShadows': maxShadows, 'pointLightShadows': pointLightShadows };

    }

    this.getParameters = function ( material, lights, fog, object ) {

        var shaderID = shaderIDs[ material.type ];
        // heuristics to create shader parameters according to lights in the scene
        // (not to blow over maxLights budget)

        var maxLightCount = allocateLights( lights );
        var allocatedShadows = allocateShadows( lights );
        var maxBones = allocateBones( object );
        var precision = renderer.getPrecision();

        if ( material.precision !== null ) {

            precision = capabilities.getMaxPrecision( material.precision );

            if ( precision !== material.precision ) {

                console.warn( 'THREE.WebGLRenderer.initMaterial:', material.precision, 'not supported, using', precision, 'instead.' );

            }

        }

        var parameters = {

            shaderID: shaderID,

            precision: precision,
            supportsVertexTextures: capabilities.vertexTextures,

            map: !! material.map,
            envMap: !! material.envMap,
            envMapMode: material.envMap && material.envMap.mapping,
            lightMap: !! material.lightMap,
            aoMap: !! material.aoMap,
            emissiveMap: !! material.emissiveMap,
            bumpMap: !! material.bumpMap,
            normalMap: !! material.normalMap,
            displacementMap: !! material.displacementMap,
            specularMap: !! material.specularMap,
            alphaMap: !! material.alphaMap,

            combine: material.combine,

            vertexColors: material.vertexColors,

            fog: fog,
            useFog: material.fog,
            fogExp: fog instanceof THREE.FogExp2,

            flatShading: material.shading === THREE.FlatShading,

            sizeAttenuation: material.sizeAttenuation,
            logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer,

            skinning: material.skinning,
            maxBones: maxBones,
            useVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture,

            morphTargets: material.morphTargets,
            morphNormals: material.morphNormals,
            maxMorphTargets: renderer.maxMorphTargets,
            maxMorphNormals: renderer.maxMorphNormals,

            maxDirLights: maxLightCount.directional,
            maxPointLights: maxLightCount.point,
            maxSpotLights: maxLightCount.spot,
            maxHemiLights: maxLightCount.hemi,

            maxShadows: allocatedShadows.maxShadows,
            pointLightShadows: allocatedShadows.pointLightShadows,
            shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && allocatedShadows.maxShadows > 0,
            shadowMapType: renderer.shadowMap.type,
            shadowMapDebug: renderer.shadowMap.debug,

            alphaTest: material.alphaTest,
            metal: material.metal,
            doubleSided: material.side === THREE.DoubleSide,
            flipSided: material.side === THREE.BackSide

        };

        return parameters;

    };

    this.getProgramCode = function ( material, parameters ) {

        var chunks = [];

        if ( parameters.shaderID ) {

            chunks.push( parameters.shaderID );

        } else {

            chunks.push( material.fragmentShader );
            chunks.push( material.vertexShader );

        }

        if ( material.defines !== undefined ) {

            for ( var name in material.defines ) {

                chunks.push( name );
                chunks.push( material.defines[ name ] );

            }

        }

        for ( var i = 0; i < parameterNames.length; i ++ ) {

            var parameterName = parameterNames[ i ];
            chunks.push( parameterName );
            chunks.push( parameters[ parameterName ] );

        }

        return chunks.join();

    };

    this.acquireProgram = function ( material, parameters, code ) {

        var program;

        // Check if code has been already compiled
        for ( var p = 0, pl = programs.length; p < pl; p ++ ) {

            var programInfo = programs[ p ];

            if ( programInfo.code === code ) {

                program = programInfo;
                ++ program.usedTimes;

                break;

            }

        }

        if ( program === undefined ) {

            program = new THREE.WebGLProgram( renderer, code, material, parameters );
            programs.push( program );

        }

        return program;

    };

    this.releaseProgram = function( program ) {

        if ( -- program.usedTimes === 0 ) {

            // Remove from unordered set
            var i = programs.indexOf( program );
            programs[ i ] = programs[ programs.length - 1 ];
            programs.pop();

            // Free WebGL resources
            program.destroy();

        }

    };

    // Exposed for resource monitoring & error feedback via renderer.info:
    this.programs = programs;

};

// File:src/renderers/webgl/WebGLProperties.js

/**
 * @author fordacious / fordacious.github.io
 */

THREE.WebGLProperties = function () {

    var properties = {};

    this.get = function ( object ) {

        var uuid = object.uuid;
        var map = properties[ uuid ];

        if ( map === undefined ) {

            map = {};
            properties[ uuid ] = map;

        }

        return map;

    };

    this.delete = function ( object ) {

        delete properties[ object.uuid ];

    };

    this.clear = function () {

        properties = {};

    };

};

// File:src/renderers/webgl/WebGLShader.js

THREE.WebGLShader = ( function () {

    function addLineNumbers( string ) {

        var lines = string.split( '\n' );

        for ( var i = 0; i < lines.length; i ++ ) {

            lines[ i ] = ( i + 1 ) + ': ' + lines[ i ];

        }

        return lines.join( '\n' );

    }

    return function WebGLShader( gl, type, string ) {

        var shader = gl.createShader( type );

        gl.shaderSource( shader, string );
        gl.compileShader( shader );

        if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) {

            console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' );

        }

        if ( gl.getShaderInfoLog( shader ) !== '' ) {

            console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) );

        }

        // --enable-privileged-webgl-extension
        // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );

        return shader;

    };

} )();

// File:src/renderers/webgl/WebGLShadowMap.js

/**
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 */

THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {

    var _gl = _renderer.context,
        _state = _renderer.state,
        _frustum = new THREE.Frustum(),
        _projScreenMatrix = new THREE.Matrix4(),

        _min = new THREE.Vector3(),
        _max = new THREE.Vector3(),

        _lookTarget = new THREE.Vector3(),
        _lightPositionWorld = new THREE.Vector3(),

        _renderList = [],

        _MorphingFlag = 1,
        _SkinningFlag = 2,

        _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1,

        _depthMaterials = new Array( _NumberOfMaterialVariants ),
        _distanceMaterials = new Array( _NumberOfMaterialVariants );

    var cubeDirections = [
        new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( - 1, 0, 0 ), new THREE.Vector3( 0, 0, 1 ),
        new THREE.Vector3( 0, 0, - 1 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, - 1, 0 )
    ];

    var cubeUps = [
        new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ),
        new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ),	new THREE.Vector3( 0, 0, - 1 )
    ];

    var cube2DViewPorts = [
        new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(),
        new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4()
    ];

    var _vector4 = new THREE.Vector4();

    // init

    var depthShader = THREE.ShaderLib[ "depthRGBA" ];
    var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );

    var distanceShader = THREE.ShaderLib[ "distanceRGBA" ];
    var distanceUniforms = THREE.UniformsUtils.clone( distanceShader.uniforms );

    for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) {

        var useMorphing = ( i & _MorphingFlag ) !== 0;
        var useSkinning = ( i & _SkinningFlag ) !== 0;

        var depthMaterial = new THREE.ShaderMaterial( {
            uniforms: depthUniforms,
            vertexShader: depthShader.vertexShader,
            fragmentShader: depthShader.fragmentShader,
            morphTargets: useMorphing,
            skinning: useSkinning
        } );

        depthMaterial._shadowPass = true;

        _depthMaterials[ i ] = depthMaterial;

        var distanceMaterial = new THREE.ShaderMaterial( {
            uniforms: distanceUniforms,
            vertexShader: distanceShader.vertexShader,
            fragmentShader: distanceShader.fragmentShader,
            morphTargets: useMorphing,
            skinning: useSkinning
        } );

        distanceMaterial._shadowPass = true;

        _distanceMaterials[ i ] = distanceMaterial;

    }

    //

    var scope = this;

    this.enabled = false;

    this.autoUpdate = true;
    this.needsUpdate = false;

    this.type = THREE.PCFShadowMap;
    this.cullFace = THREE.CullFaceFront;

    this.render = function ( scene ) {

        var faceCount, isPointLight;

        if ( scope.enabled === false ) return;
        if ( scope.autoUpdate === false && scope.needsUpdate === false ) return;

        // Set GL state for depth map.
        _gl.clearColor( 1, 1, 1, 1 );
        _state.disable( _gl.BLEND );
        _state.enable( _gl.CULL_FACE );
        _gl.frontFace( _gl.CCW );
        _gl.cullFace( scope.cullFace === THREE.CullFaceFront ? _gl.FRONT : _gl.BACK );
        _state.setDepthTest( true );

        // save the existing viewport so it can be restored later
        _renderer.getViewport( _vector4 );

        // render depth map

        for ( var i = 0, il = _lights.length; i < il; i ++ ) {

            var light = _lights[ i ];

            if ( light.castShadow === true ) {

                var shadow = light.shadow;
                var shadowCamera = shadow.camera;
                var shadowMapSize = shadow.mapSize;

                if ( light instanceof THREE.PointLight ) {

                    faceCount = 6;
                    isPointLight = true;

                    var vpWidth = shadowMapSize.x / 4.0;
                    var vpHeight = shadowMapSize.y / 2.0;

                    // These viewports map a cube-map onto a 2D texture with the
                    // following orientation:
                    //
                    //  xzXZ
                    //   y Y
                    //
                    // X - Positive x direction
                    // x - Negative x direction
                    // Y - Positive y direction
                    // y - Negative y direction
                    // Z - Positive z direction
                    // z - Negative z direction

                    // positive X
                    cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight );
                    // negative X
                    cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight );
                    // positive Z
                    cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight );
                    // negative Z
                    cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight );
                    // positive Y
                    cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight );
                    // negative Y
                    cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight );

                } else {

                    faceCount = 1;
                    isPointLight = false;

                }

                if ( shadow.map === null ) {

                    var shadowFilter = THREE.LinearFilter;

                    if ( scope.type === THREE.PCFSoftShadowMap ) {

                        shadowFilter = THREE.NearestFilter;

                    }

                    var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };

                    shadow.map = new THREE.WebGLRenderTarget( shadowMapSize.x, shadowMapSize.y, pars );
                    shadow.matrix = new THREE.Matrix4();

                    //

                    if ( light instanceof THREE.SpotLight ) {

                        shadowCamera.aspect = shadowMapSize.x / shadowMapSize.y;

                    }

                    shadowCamera.updateProjectionMatrix();

                }

                var shadowMap = shadow.map;
                var shadowMatrix = shadow.matrix;

                _lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
                shadowCamera.position.copy( _lightPositionWorld );

                _renderer.setRenderTarget( shadowMap );
                _renderer.clear();

                // render shadow map for each cube face (if omni-directional) or
                // run a single pass if not

                for ( var face = 0; face < faceCount; face ++ ) {

                    if ( isPointLight ) {

                        _lookTarget.copy( shadowCamera.position );
                        _lookTarget.add( cubeDirections[ face ] );
                        shadowCamera.up.copy( cubeUps[ face ] );
                        shadowCamera.lookAt( _lookTarget );
                        var vpDimensions = cube2DViewPorts[ face ];
                        _renderer.setViewport( vpDimensions.x, vpDimensions.y, vpDimensions.z, vpDimensions.w );

                    } else {

                        _lookTarget.setFromMatrixPosition( light.target.matrixWorld );
                        shadowCamera.lookAt( _lookTarget );

                    }

                    shadowCamera.updateMatrixWorld();
                    shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );

                    // compute shadow matrix

                    shadowMatrix.set(
                        0.5, 0.0, 0.0, 0.5,
                        0.0, 0.5, 0.0, 0.5,
                        0.0, 0.0, 0.5, 0.5,
                        0.0, 0.0, 0.0, 1.0
                    );

                    shadowMatrix.multiply( shadowCamera.projectionMatrix );
                    shadowMatrix.multiply( shadowCamera.matrixWorldInverse );

                    // update camera matrices and frustum

                    _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
                    _frustum.setFromMatrix( _projScreenMatrix );

                    // set object matrices & frustum culling

                    _renderList.length = 0;

                    projectObject( scene, shadowCamera );

                    // render shadow map
                    // render regular objects

                    for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) {

                        var object = _renderList[ j ];
                        var geometry = _objects.update( object );
                        var material = object.material;

                        if ( material instanceof THREE.MeshFaceMaterial ) {

                            var groups = geometry.groups;
                            var materials = material.materials;

                            for ( var k = 0, kl = groups.length; k < kl; k ++ ) {

                                var group = groups[ k ];
                                var groupMaterial = materials[ group.materialIndex ];

                                if ( groupMaterial.visible === true ) {

                                    var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld );
                                    _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, group );

                                }

                            }

                        } else {

                            var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld );
                            _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, null );

                        }

                    }

                }

                // We must call _renderer.resetGLState() at the end of each iteration of
                // the light loop in order to force material updates for each light.
                _renderer.resetGLState();

            }

        }

        _renderer.setViewport( _vector4.x, _vector4.y, _vector4.z, _vector4.w );

        // Restore GL state.
        var clearColor = _renderer.getClearColor(),
            clearAlpha = _renderer.getClearAlpha();
        _renderer.setClearColor( clearColor, clearAlpha );
        _state.enable( _gl.BLEND );

        if ( scope.cullFace === THREE.CullFaceFront ) {

            _gl.cullFace( _gl.BACK );

        }

        _renderer.resetGLState();

        scope.needsUpdate = false;

    };

    function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) {

        var geometry = object.geometry;

        var newMaterial = null;

        var materialVariants = _depthMaterials;
        var customMaterial = object.customDepthMaterial;

        if ( isPointLight ) {

            materialVariants = _distanceMaterials;
            customMaterial = object.customDistanceMaterial;

        }

        if ( ! customMaterial ) {

            var useMorphing = geometry.morphTargets !== undefined &&
                geometry.morphTargets.length > 0 && material.morphTargets;

            var useSkinning = object instanceof THREE.SkinnedMesh && material.skinning;

            var variantIndex = 0;

            if ( useMorphing ) variantIndex |= _MorphingFlag;
            if ( useSkinning ) variantIndex |= _SkinningFlag;

            newMaterial = materialVariants[ variantIndex ];

        } else {

            newMaterial = customMaterial;

        }

        newMaterial.visible = material.visible;
        newMaterial.wireframe = material.wireframe;
        newMaterial.wireframeLinewidth = material.wireframeLinewidth;

        if ( isPointLight && newMaterial.uniforms.lightPos !== undefined ) {

            newMaterial.uniforms.lightPos.value.copy( lightPositionWorld );

        }

        return newMaterial;

    }

    function projectObject( object, camera ) {

        if ( object.visible === false ) return;

        if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) {

            if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) {

                var material = object.material;

                if ( material.visible === true ) {

                    object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
                    _renderList.push( object );

                }

            }

        }

        var children = object.children;

        for ( var i = 0, l = children.length; i < l; i ++ ) {

            projectObject( children[ i ], camera );

        }

    }

};

// File:src/renderers/webgl/WebGLState.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.WebGLState = function ( gl, extensions, paramThreeToGL ) {

    var _this = this;

    var newAttributes = new Uint8Array( 16 );
    var enabledAttributes = new Uint8Array( 16 );
    var attributeDivisors = new Uint8Array( 16 );

    var capabilities = {};

    var compressedTextureFormats = null;

    var currentBlending = null;
    var currentBlendEquation = null;
    var currentBlendSrc = null;
    var currentBlendDst = null;
    var currentBlendEquationAlpha = null;
    var currentBlendSrcAlpha = null;
    var currentBlendDstAlpha = null;

    var currentDepthFunc = null;
    var currentDepthWrite = null;

    var currentColorWrite = null;

    var currentFlipSided = null;

    var currentLineWidth = null;

    var currentPolygonOffsetFactor = null;
    var currentPolygonOffsetUnits = null;

    var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );

    var currentTextureSlot = undefined;
    var currentBoundTextures = {};

    this.init = function () {

        gl.clearColor( 0, 0, 0, 1 );
        gl.clearDepth( 1 );
        gl.clearStencil( 0 );

        this.enable( gl.DEPTH_TEST );
        gl.depthFunc( gl.LEQUAL );

        gl.frontFace( gl.CCW );
        gl.cullFace( gl.BACK );
        this.enable( gl.CULL_FACE );

        this.enable( gl.BLEND );
        gl.blendEquation( gl.FUNC_ADD );
        gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );

    };

    this.initAttributes = function () {

        for ( var i = 0, l = newAttributes.length; i < l; i ++ ) {

            newAttributes[ i ] = 0;

        }

    };

    this.enableAttribute = function ( attribute ) {

        newAttributes[ attribute ] = 1;

        if ( enabledAttributes[ attribute ] === 0 ) {

            gl.enableVertexAttribArray( attribute );
            enabledAttributes[ attribute ] = 1;

        }

        if ( attributeDivisors[ attribute ] !== 0 ) {

            var extension = extensions.get( 'ANGLE_instanced_arrays' );

            extension.vertexAttribDivisorANGLE( attribute, 0 );
            attributeDivisors[ attribute ] = 0;

        }

    };

    this.enableAttributeAndDivisor = function ( attribute, meshPerAttribute, extension ) {

        newAttributes[ attribute ] = 1;

        if ( enabledAttributes[ attribute ] === 0 ) {

            gl.enableVertexAttribArray( attribute );
            enabledAttributes[ attribute ] = 1;

        }

        if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {

            extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute );
            attributeDivisors[ attribute ] = meshPerAttribute;

        }

    };

    this.disableUnusedAttributes = function () {

        for ( var i = 0, l = enabledAttributes.length; i < l; i ++ ) {

            if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {

                gl.disableVertexAttribArray( i );
                enabledAttributes[ i ] = 0;

            }

        }

    };

    this.enable = function ( id ) {

        if ( capabilities[ id ] !== true ) {

            gl.enable( id );
            capabilities[ id ] = true;

        }

    };

    this.disable = function ( id ) {

        if ( capabilities[ id ] !== false ) {

            gl.disable( id );
            capabilities[ id ] = false;

        }

    };

    this.getCompressedTextureFormats = function () {

        if ( compressedTextureFormats === null ) {

            compressedTextureFormats = [];

            if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||
                extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) {

                var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );

                for ( var i = 0; i < formats.length; i ++ ) {

                    compressedTextureFormats.push( formats[ i ] );

                }

            }

        }

        return compressedTextureFormats;

    };

    this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) {

        if ( blending !== currentBlending ) {

            if ( blending === THREE.NoBlending ) {

                this.disable( gl.BLEND );

            } else if ( blending === THREE.AdditiveBlending ) {

                this.enable( gl.BLEND );
                gl.blendEquation( gl.FUNC_ADD );
                gl.blendFunc( gl.SRC_ALPHA, gl.ONE );

            } else if ( blending === THREE.SubtractiveBlending ) {

                // TODO: Find blendFuncSeparate() combination

                this.enable( gl.BLEND );
                gl.blendEquation( gl.FUNC_ADD );
                gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR );

            } else if ( blending === THREE.MultiplyBlending ) {

                // TODO: Find blendFuncSeparate() combination

                this.enable( gl.BLEND );
                gl.blendEquation( gl.FUNC_ADD );
                gl.blendFunc( gl.ZERO, gl.SRC_COLOR );

            } else if ( blending === THREE.CustomBlending ) {

                this.enable( gl.BLEND );

            } else {

                this.enable( gl.BLEND );
                gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
                gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );

            }

            currentBlending = blending;

        }

        if ( blending === THREE.CustomBlending ) {

            blendEquationAlpha = blendEquationAlpha || blendEquation;
            blendSrcAlpha = blendSrcAlpha || blendSrc;
            blendDstAlpha = blendDstAlpha || blendDst;

            if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {

                gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) );

                currentBlendEquation = blendEquation;
                currentBlendEquationAlpha = blendEquationAlpha;

            }

            if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {

                gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) );

                currentBlendSrc = blendSrc;
                currentBlendDst = blendDst;
                currentBlendSrcAlpha = blendSrcAlpha;
                currentBlendDstAlpha = blendDstAlpha;

            }

        } else {

            currentBlendEquation = null;
            currentBlendSrc = null;
            currentBlendDst = null;
            currentBlendEquationAlpha = null;
            currentBlendSrcAlpha = null;
            currentBlendDstAlpha = null;

        }

    };

    this.setDepthFunc = function ( depthFunc ) {

        if ( currentDepthFunc !== depthFunc ) {

            if ( depthFunc ) {

                switch ( depthFunc ) {

                    case THREE.NeverDepth:

                        gl.depthFunc( gl.NEVER );
                        break;

                    case THREE.AlwaysDepth:

                        gl.depthFunc( gl.ALWAYS );
                        break;

                    case THREE.LessDepth:

                        gl.depthFunc( gl.LESS );
                        break;

                    case THREE.LessEqualDepth:

                        gl.depthFunc( gl.LEQUAL );
                        break;

                    case THREE.EqualDepth:

                        gl.depthFunc( gl.EQUAL );
                        break;

                    case THREE.GreaterEqualDepth:

                        gl.depthFunc( gl.GEQUAL );
                        break;

                    case THREE.GreaterDepth:

                        gl.depthFunc( gl.GREATER );
                        break;

                    case THREE.NotEqualDepth:

                        gl.depthFunc( gl.NOTEQUAL );
                        break;

                    default:

                        gl.depthFunc( gl.LEQUAL );

                }

            } else {

                gl.depthFunc( gl.LEQUAL );

            }

            currentDepthFunc = depthFunc;

        }

    };

    this.setDepthTest = function ( depthTest ) {

        if ( depthTest ) {

            this.enable( gl.DEPTH_TEST );

        } else {

            this.disable( gl.DEPTH_TEST );

        }

    };

    this.setDepthWrite = function ( depthWrite ) {

        if ( currentDepthWrite !== depthWrite ) {

            gl.depthMask( depthWrite );
            currentDepthWrite = depthWrite;

        }

    };

    this.setColorWrite = function ( colorWrite ) {

        if ( currentColorWrite !== colorWrite ) {

            gl.colorMask( colorWrite, colorWrite, colorWrite, colorWrite );
            currentColorWrite = colorWrite;

        }

    };

    this.setFlipSided = function ( flipSided ) {

        if ( currentFlipSided !== flipSided ) {

            if ( flipSided ) {

                gl.frontFace( gl.CW );

            } else {

                gl.frontFace( gl.CCW );

            }

            currentFlipSided = flipSided;

        }

    };

    this.setLineWidth = function ( width ) {

        if ( width !== currentLineWidth ) {

            gl.lineWidth( width );

            currentLineWidth = width;

        }

    };

    this.setPolygonOffset = function ( polygonOffset, factor, units ) {

        if ( polygonOffset ) {

            this.enable( gl.POLYGON_OFFSET_FILL );

        } else {

            this.disable( gl.POLYGON_OFFSET_FILL );

        }

        if ( polygonOffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) {

            gl.polygonOffset( factor, units );

            currentPolygonOffsetFactor = factor;
            currentPolygonOffsetUnits = units;

        }

    };

    this.setScissorTest = function ( scissorTest ) {

        if ( scissorTest ) {

            this.enable( gl.SCISSOR_TEST );

        } else {

            this.disable( gl.SCISSOR_TEST );

        }

    };

    // texture

    this.activeTexture = function ( webglSlot ) {

        if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;

        if ( currentTextureSlot !== webglSlot ) {

            gl.activeTexture( webglSlot );
            currentTextureSlot = webglSlot;

        }

    }

    this.bindTexture = function ( webglType, webglTexture ) {

        if ( currentTextureSlot === undefined ) {

            _this.activeTexture();

        }

        var boundTexture = currentBoundTextures[ currentTextureSlot ];

        if ( boundTexture === undefined ) {

            boundTexture = { type: undefined, texture: undefined };
            currentBoundTextures[ currentTextureSlot ] = boundTexture;

        }

        if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {

            gl.bindTexture( webglType, webglTexture );

            boundTexture.type = webglType;
            boundTexture.texture = webglTexture;

        }

    };

    this.compressedTexImage2D = function () {

        try {

            gl.compressedTexImage2D.apply( gl, arguments );

        } catch ( error ) {

            console.error( error );

        }

    };

    this.texImage2D = function () {

        try {

            gl.texImage2D.apply( gl, arguments );

        } catch ( error ) {

            console.error( error );

        }

    };

    //

    this.reset = function () {

        for ( var i = 0; i < enabledAttributes.length; i ++ ) {

            if ( enabledAttributes[ i ] === 1 ) {

                gl.disableVertexAttribArray( i );
                enabledAttributes[ i ] = 0;

            }

        }

        capabilities = {};

        compressedTextureFormats = null;

        currentBlending = null;

        currentDepthWrite = null;
        currentColorWrite = null;

        currentFlipSided = null;

    };

};

// File:src/renderers/webgl/plugins/LensFlarePlugin.js

/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.LensFlarePlugin = function ( renderer, flares ) {

    var gl = renderer.context;
    var state = renderer.state;

    var vertexBuffer, elementBuffer;
    var program, attributes, uniforms;
    var hasVertexTexture;

    var tempTexture, occlusionTexture;

    function init() {

        var vertices = new Float32Array( [
            - 1, - 1,  0, 0,
            1, - 1,  1, 0,
            1,  1,  1, 1,
            - 1,  1,  0, 1
        ] );

        var faces = new Uint16Array( [
            0, 1, 2,
            0, 2, 3
        ] );

        // buffers

        vertexBuffer     = gl.createBuffer();
        elementBuffer    = gl.createBuffer();

        gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
        gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );

        gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
        gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );

        // textures

        tempTexture      = gl.createTexture();
        occlusionTexture = gl.createTexture();

        state.bindTexture( gl.TEXTURE_2D, tempTexture );
        gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );

        state.bindTexture( gl.TEXTURE_2D, occlusionTexture );
        gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
        gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );

        hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0;

        var shader;

        if ( hasVertexTexture ) {

            shader = {

                vertexShader: [

                    "uniform lowp int renderType;",

                    "uniform vec3 screenPosition;",
                    "uniform vec2 scale;",
                    "uniform float rotation;",

                    "uniform sampler2D occlusionMap;",

                    "attribute vec2 position;",
                    "attribute vec2 uv;",

                    "varying vec2 vUV;",
                    "varying float vVisibility;",

                    "void main() {",

                    "vUV = uv;",

                    "vec2 pos = position;",

                    "if ( renderType == 2 ) {",

                    "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );",
                    "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );",
                    "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );",
                    "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );",
                    "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );",
                    "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );",
                    "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );",
                    "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );",
                    "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );",

                    "vVisibility =        visibility.r / 9.0;",
                    "vVisibility *= 1.0 - visibility.g / 9.0;",
                    "vVisibility *=       visibility.b / 9.0;",
                    "vVisibility *= 1.0 - visibility.a / 9.0;",

                    "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
                    "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",

                    "}",

                    "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",

                    "}"

                ].join( "\n" ),

                fragmentShader: [

                    "uniform lowp int renderType;",

                    "uniform sampler2D map;",
                    "uniform float opacity;",
                    "uniform vec3 color;",

                    "varying vec2 vUV;",
                    "varying float vVisibility;",

                    "void main() {",

                    // pink square

                    "if ( renderType == 0 ) {",

                    "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",

                    // restore

                    "} else if ( renderType == 1 ) {",

                    "gl_FragColor = texture2D( map, vUV );",

                    // flare

                    "} else {",

                    "vec4 texture = texture2D( map, vUV );",
                    "texture.a *= opacity * vVisibility;",
                    "gl_FragColor = texture;",
                    "gl_FragColor.rgb *= color;",

                    "}",

                    "}"

                ].join( "\n" )

            };

        } else {

            shader = {

                vertexShader: [

                    "uniform lowp int renderType;",

                    "uniform vec3 screenPosition;",
                    "uniform vec2 scale;",
                    "uniform float rotation;",

                    "attribute vec2 position;",
                    "attribute vec2 uv;",

                    "varying vec2 vUV;",

                    "void main() {",

                    "vUV = uv;",

                    "vec2 pos = position;",

                    "if ( renderType == 2 ) {",

                    "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
                    "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",

                    "}",

                    "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",

                    "}"

                ].join( "\n" ),

                fragmentShader: [

                    "precision mediump float;",

                    "uniform lowp int renderType;",

                    "uniform sampler2D map;",
                    "uniform sampler2D occlusionMap;",
                    "uniform float opacity;",
                    "uniform vec3 color;",

                    "varying vec2 vUV;",

                    "void main() {",

                    // pink square

                    "if ( renderType == 0 ) {",

                    "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",

                    // restore

                    "} else if ( renderType == 1 ) {",

                    "gl_FragColor = texture2D( map, vUV );",

                    // flare

                    "} else {",

                    "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;",
                    "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;",
                    "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;",
                    "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;",
                    "visibility = ( 1.0 - visibility / 4.0 );",

                    "vec4 texture = texture2D( map, vUV );",
                    "texture.a *= opacity * visibility;",
                    "gl_FragColor = texture;",
                    "gl_FragColor.rgb *= color;",

                    "}",

                    "}"

                ].join( "\n" )

            };

        }

        program = createProgram( shader );

        attributes = {
            vertex: gl.getAttribLocation ( program, "position" ),
            uv:     gl.getAttribLocation ( program, "uv" )
        };

        uniforms = {
            renderType:     gl.getUniformLocation( program, "renderType" ),
            map:            gl.getUniformLocation( program, "map" ),
            occlusionMap:   gl.getUniformLocation( program, "occlusionMap" ),
            opacity:        gl.getUniformLocation( program, "opacity" ),
            color:          gl.getUniformLocation( program, "color" ),
            scale:          gl.getUniformLocation( program, "scale" ),
            rotation:       gl.getUniformLocation( program, "rotation" ),
            screenPosition: gl.getUniformLocation( program, "screenPosition" )
        };

    }

    /*
	 * Render lens flares
	 * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
	 *         reads these back and calculates occlusion.
	 */

    this.render = function ( scene, camera, viewportWidth, viewportHeight ) {

        if ( flares.length === 0 ) return;

        var tempPosition = new THREE.Vector3();

        var invAspect = viewportHeight / viewportWidth,
            halfViewportWidth = viewportWidth * 0.5,
            halfViewportHeight = viewportHeight * 0.5;

        var size = 16 / viewportHeight,
            scale = new THREE.Vector2( size * invAspect, size );

        var screenPosition = new THREE.Vector3( 1, 1, 0 ),
            screenPositionPixels = new THREE.Vector2( 1, 1 );

        if ( program === undefined ) {

            init();

        }

        gl.useProgram( program );

        state.initAttributes();
        state.enableAttribute( attributes.vertex );
        state.enableAttribute( attributes.uv );
        state.disableUnusedAttributes();

        // loop through all lens flares to update their occlusion and positions
        // setup gl and common used attribs/uniforms

        gl.uniform1i( uniforms.occlusionMap, 0 );
        gl.uniform1i( uniforms.map, 1 );

        gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
        gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 );
        gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );

        gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );

        state.disable( gl.CULL_FACE );
        gl.depthMask( false );

        for ( var i = 0, l = flares.length; i < l; i ++ ) {

            size = 16 / viewportHeight;
            scale.set( size * invAspect, size );

            // calc object screen position

            var flare = flares[ i ];

            tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] );

            tempPosition.applyMatrix4( camera.matrixWorldInverse );
            tempPosition.applyProjection( camera.projectionMatrix );

            // setup arrays for gl programs

            screenPosition.copy( tempPosition );

            screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
            screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;

            // screen cull

            if ( hasVertexTexture || (
                screenPositionPixels.x > 0 &&
                screenPositionPixels.x < viewportWidth &&
                screenPositionPixels.y > 0 &&
                screenPositionPixels.y < viewportHeight ) ) {

                // save current RGB to temp texture

                state.activeTexture( gl.TEXTURE0 );
                state.bindTexture( gl.TEXTURE_2D, null );
                state.activeTexture( gl.TEXTURE1 );
                state.bindTexture( gl.TEXTURE_2D, tempTexture );
                gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );


                // render pink quad

                gl.uniform1i( uniforms.renderType, 0 );
                gl.uniform2f( uniforms.scale, scale.x, scale.y );
                gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );

                state.disable( gl.BLEND );
                state.enable( gl.DEPTH_TEST );

                gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );


                // copy result to occlusionMap

                state.activeTexture( gl.TEXTURE0 );
                state.bindTexture( gl.TEXTURE_2D, occlusionTexture );
                gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );


                // restore graphics

                gl.uniform1i( uniforms.renderType, 1 );
                state.disable( gl.DEPTH_TEST );

                state.activeTexture( gl.TEXTURE1 );
                state.bindTexture( gl.TEXTURE_2D, tempTexture );
                gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );


                // update object positions

                flare.positionScreen.copy( screenPosition );

                if ( flare.customUpdateCallback ) {

                    flare.customUpdateCallback( flare );

                } else {

                    flare.updateLensFlares();

                }

                // render flares

                gl.uniform1i( uniforms.renderType, 2 );
                state.enable( gl.BLEND );

                for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {

                    var sprite = flare.lensFlares[ j ];

                    if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {

                        screenPosition.x = sprite.x;
                        screenPosition.y = sprite.y;
                        screenPosition.z = sprite.z;

                        size = sprite.size * sprite.scale / viewportHeight;

                        scale.x = size * invAspect;
                        scale.y = size;

                        gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
                        gl.uniform2f( uniforms.scale, scale.x, scale.y );
                        gl.uniform1f( uniforms.rotation, sprite.rotation );

                        gl.uniform1f( uniforms.opacity, sprite.opacity );
                        gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );

                        state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
                        renderer.setTexture( sprite.texture, 1 );

                        gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );

                    }

                }

            }

        }

        // restore gl

        state.enable( gl.CULL_FACE );
        state.enable( gl.DEPTH_TEST );
        gl.depthMask( true );

        renderer.resetGLState();

    };

    function createProgram ( shader ) {

        var program = gl.createProgram();

        var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );
        var vertexShader = gl.createShader( gl.VERTEX_SHADER );

        var prefix = "precision " + renderer.getPrecision() + " float;\n";

        gl.shaderSource( fragmentShader, prefix + shader.fragmentShader );
        gl.shaderSource( vertexShader, prefix + shader.vertexShader );

        gl.compileShader( fragmentShader );
        gl.compileShader( vertexShader );

        gl.attachShader( program, fragmentShader );
        gl.attachShader( program, vertexShader );

        gl.linkProgram( program );

        return program;

    }

};

// File:src/renderers/webgl/plugins/SpritePlugin.js

/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.SpritePlugin = function ( renderer, sprites ) {

    var gl = renderer.context;
    var state = renderer.state;

    var vertexBuffer, elementBuffer;
    var program, attributes, uniforms;

    var texture;

    // decompose matrixWorld

    var spritePosition = new THREE.Vector3();
    var spriteRotation = new THREE.Quaternion();
    var spriteScale = new THREE.Vector3();

    function init() {

        var vertices = new Float32Array( [
            - 0.5, - 0.5,  0, 0,
            0.5, - 0.5,  1, 0,
            0.5,   0.5,  1, 1,
            - 0.5,   0.5,  0, 1
        ] );

        var faces = new Uint16Array( [
            0, 1, 2,
            0, 2, 3
        ] );

        vertexBuffer  = gl.createBuffer();
        elementBuffer = gl.createBuffer();

        gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
        gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );

        gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );
        gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );

        program = createProgram();

        attributes = {
            position:			gl.getAttribLocation ( program, 'position' ),
            uv:					gl.getAttribLocation ( program, 'uv' )
        };

        uniforms = {
            uvOffset:			gl.getUniformLocation( program, 'uvOffset' ),
            uvScale:			gl.getUniformLocation( program, 'uvScale' ),

            rotation:			gl.getUniformLocation( program, 'rotation' ),
            scale:				gl.getUniformLocation( program, 'scale' ),

            color:				gl.getUniformLocation( program, 'color' ),
            map:				gl.getUniformLocation( program, 'map' ),
            opacity:			gl.getUniformLocation( program, 'opacity' ),

            modelViewMatrix: 	gl.getUniformLocation( program, 'modelViewMatrix' ),
            projectionMatrix:	gl.getUniformLocation( program, 'projectionMatrix' ),

            fogType:			gl.getUniformLocation( program, 'fogType' ),
            fogDensity:			gl.getUniformLocation( program, 'fogDensity' ),
            fogNear:			gl.getUniformLocation( program, 'fogNear' ),
            fogFar:				gl.getUniformLocation( program, 'fogFar' ),
            fogColor:			gl.getUniformLocation( program, 'fogColor' ),

            alphaTest:			gl.getUniformLocation( program, 'alphaTest' )
        };

        var canvas = document.createElement( 'canvas' );
        canvas.width = 8;
        canvas.height = 8;

        var context = canvas.getContext( '2d' );
        context.fillStyle = 'white';
        context.fillRect( 0, 0, 8, 8 );

        texture = new THREE.Texture( canvas );
        texture.needsUpdate = true;

    }

    this.render = function ( scene, camera ) {

        if ( sprites.length === 0 ) return;

        // setup gl

        if ( program === undefined ) {

            init();

        }

        gl.useProgram( program );

        state.initAttributes();
        state.enableAttribute( attributes.position );
        state.enableAttribute( attributes.uv );
        state.disableUnusedAttributes();

        state.disable( gl.CULL_FACE );
        state.enable( gl.BLEND );

        gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );
        gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 );
        gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );

        gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );

        gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );

        state.activeTexture( gl.TEXTURE0 );
        gl.uniform1i( uniforms.map, 0 );

        var oldFogType = 0;
        var sceneFogType = 0;
        var fog = scene.fog;

        if ( fog ) {

            gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );

            if ( fog instanceof THREE.Fog ) {

                gl.uniform1f( uniforms.fogNear, fog.near );
                gl.uniform1f( uniforms.fogFar, fog.far );

                gl.uniform1i( uniforms.fogType, 1 );
                oldFogType = 1;
                sceneFogType = 1;

            } else if ( fog instanceof THREE.FogExp2 ) {

                gl.uniform1f( uniforms.fogDensity, fog.density );

                gl.uniform1i( uniforms.fogType, 2 );
                oldFogType = 2;
                sceneFogType = 2;

            }

        } else {

            gl.uniform1i( uniforms.fogType, 0 );
            oldFogType = 0;
            sceneFogType = 0;

        }


        // update positions and sort

        for ( var i = 0, l = sprites.length; i < l; i ++ ) {

            var sprite = sprites[ i ];

            sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
            sprite.z = - sprite.modelViewMatrix.elements[ 14 ];

        }

        sprites.sort( painterSortStable );

        // render all sprites

        var scale = [];

        for ( var i = 0, l = sprites.length; i < l; i ++ ) {

            var sprite = sprites[ i ];
            var material = sprite.material;

            gl.uniform1f( uniforms.alphaTest, material.alphaTest );
            gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements );

            sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale );

            scale[ 0 ] = spriteScale.x;
            scale[ 1 ] = spriteScale.y;

            var fogType = 0;

            if ( scene.fog && material.fog ) {

                fogType = sceneFogType;

            }

            if ( oldFogType !== fogType ) {

                gl.uniform1i( uniforms.fogType, fogType );
                oldFogType = fogType;

            }

            if ( material.map !== null ) {

                gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y );
                gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y );

            } else {

                gl.uniform2f( uniforms.uvOffset, 0, 0 );
                gl.uniform2f( uniforms.uvScale, 1, 1 );

            }

            gl.uniform1f( uniforms.opacity, material.opacity );
            gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );

            gl.uniform1f( uniforms.rotation, material.rotation );
            gl.uniform2fv( uniforms.scale, scale );

            state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
            state.setDepthTest( material.depthTest );
            state.setDepthWrite( material.depthWrite );

            if ( material.map && material.map.image && material.map.image.width ) {

                renderer.setTexture( material.map, 0 );

            } else {

                renderer.setTexture( texture, 0 );

            }

            gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );

        }

        // restore gl

        state.enable( gl.CULL_FACE );

        renderer.resetGLState();

    };

    function createProgram () {

        var program = gl.createProgram();

        var vertexShader = gl.createShader( gl.VERTEX_SHADER );
        var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );

        gl.shaderSource( vertexShader, [

            'precision ' + renderer.getPrecision() + ' float;',

            'uniform mat4 modelViewMatrix;',
            'uniform mat4 projectionMatrix;',
            'uniform float rotation;',
            'uniform vec2 scale;',
            'uniform vec2 uvOffset;',
            'uniform vec2 uvScale;',

            'attribute vec2 position;',
            'attribute vec2 uv;',

            'varying vec2 vUV;',

            'void main() {',

            'vUV = uvOffset + uv * uvScale;',

            'vec2 alignedPosition = position * scale;',

            'vec2 rotatedPosition;',
            'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',
            'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',

            'vec4 finalPosition;',

            'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
            'finalPosition.xy += rotatedPosition;',
            'finalPosition = projectionMatrix * finalPosition;',

            'gl_Position = finalPosition;',

            '}'

        ].join( '\n' ) );

        gl.shaderSource( fragmentShader, [

            'precision ' + renderer.getPrecision() + ' float;',

            'uniform vec3 color;',
            'uniform sampler2D map;',
            'uniform float opacity;',

            'uniform int fogType;',
            'uniform vec3 fogColor;',
            'uniform float fogDensity;',
            'uniform float fogNear;',
            'uniform float fogFar;',
            'uniform float alphaTest;',

            'varying vec2 vUV;',

            'void main() {',

            'vec4 texture = texture2D( map, vUV );',

            'if ( texture.a < alphaTest ) discard;',

            'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',

            'if ( fogType > 0 ) {',

            'float depth = gl_FragCoord.z / gl_FragCoord.w;',
            'float fogFactor = 0.0;',

            'if ( fogType == 1 ) {',

            'fogFactor = smoothstep( fogNear, fogFar, depth );',

            '} else {',

            'const float LOG2 = 1.442695;',
            'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );',
            'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',

            '}',

            'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );',

            '}',

            '}'

        ].join( '\n' ) );

        gl.compileShader( vertexShader );
        gl.compileShader( fragmentShader );

        gl.attachShader( program, vertexShader );
        gl.attachShader( program, fragmentShader );

        gl.linkProgram( program );

        return program;

    }

    function painterSortStable ( a, b ) {

        if ( a.z !== b.z ) {

            return b.z - a.z;

        } else {

            return b.id - a.id;

        }

    }

};

// File:src/extras/CurveUtils.js

/**
 * @author zz85 / http://www.lab4games.net/zz85/blog
 */

THREE.CurveUtils = {

    tangentQuadraticBezier: function ( t, p0, p1, p2 ) {

        return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );

    },

    // Puay Bing, thanks for helping with this derivative!

    tangentCubicBezier: function ( t, p0, p1, p2, p3 ) {

        return - 3 * p0 * ( 1 - t ) * ( 1 - t )  +
            3 * p1 * ( 1 - t ) * ( 1 - t ) - 6 * t * p1 * ( 1 - t ) +
            6 * t *  p2 * ( 1 - t ) - 3 * t * t * p2 +
            3 * t * t * p3;

    },

    tangentSpline: function ( t, p0, p1, p2, p3 ) {

        // To check if my formulas are correct

        var h00 = 6 * t * t - 6 * t; 	// derived from 2t^3 − 3t^2 + 1
        var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
        var h01 = - 6 * t * t + 6 * t; 	// − 2t3 + 3t2
        var h11 = 3 * t * t - 2 * t;	// t3 − t2

        return h00 + h10 + h01 + h11;

    },

    // Catmull-Rom

    interpolate: function( p0, p1, p2, p3, t ) {

        var v0 = ( p2 - p0 ) * 0.5;
        var v1 = ( p3 - p1 ) * 0.5;
        var t2 = t * t;
        var t3 = t * t2;
        return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;

    }

};

// File:src/extras/GeometryUtils.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.GeometryUtils = {

    merge: function ( geometry1, geometry2, materialIndexOffset ) {

        console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );

        var matrix;

        if ( geometry2 instanceof THREE.Mesh ) {

            geometry2.matrixAutoUpdate && geometry2.updateMatrix();

            matrix = geometry2.matrix;
            geometry2 = geometry2.geometry;

        }

        geometry1.merge( geometry2, matrix, materialIndexOffset );

    },

    center: function ( geometry ) {

        console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );
        return geometry.center();

    }

};

// File:src/extras/ImageUtils.js

/**
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 * @author Daosheng Mu / https://github.com/DaoshengMu/
 */

THREE.ImageUtils = {

    crossOrigin: undefined,

    loadTexture: function ( url, mapping, onLoad, onError ) {

        console.warn( 'THREE.ImageUtils.loadTexture is being deprecated. Use THREE.TextureLoader() instead.' );

        var loader = new THREE.TextureLoader();
        loader.setCrossOrigin( this.crossOrigin );

        var texture = loader.load( url, onLoad, undefined, onError );

        if ( mapping ) texture.mapping = mapping;

        return texture;

    },

    loadTextureCube: function ( urls, mapping, onLoad, onError ) {

        console.warn( 'THREE.ImageUtils.loadTextureCube is being deprecated. Use THREE.CubeTextureLoader() instead.' );

        var loader = new THREE.CubeTextureLoader();
        loader.setCrossOrigin( this.crossOrigin );

        var texture = loader.load( urls, onLoad, undefined, onError );

        if ( mapping ) texture.mapping = mapping;

        return texture;

    },

    loadCompressedTexture: function () {

        console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' )

    },

    loadCompressedTextureCube: function () {

        console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' )

    }

};

// File:src/extras/SceneUtils.js

/**
 * @author alteredq / http://alteredqualia.com/
 */

THREE.SceneUtils = {

    createMultiMaterialObject: function ( geometry, materials ) {

        var group = new THREE.Group();

        for ( var i = 0, l = materials.length; i < l; i ++ ) {

            group.add( new THREE.Mesh( geometry, materials[ i ] ) );

        }

        return group;

    },

    detach: function ( child, parent, scene ) {

        child.applyMatrix( parent.matrixWorld );
        parent.remove( child );
        scene.add( child );

    },

    attach: function ( child, scene, parent ) {

        var matrixWorldInverse = new THREE.Matrix4();
        matrixWorldInverse.getInverse( parent.matrixWorld );
        child.applyMatrix( matrixWorldInverse );

        scene.remove( child );
        parent.add( child );

    }

};

// File:src/extras/ShapeUtils.js

/**
 * @author zz85 / http://www.lab4games.net/zz85/blog
 */

THREE.ShapeUtils = {

    // calculate area of the contour polygon

    area: function ( contour ) {

        var n = contour.length;
        var a = 0.0;

        for ( var p = n - 1, q = 0; q < n; p = q ++ ) {

            a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;

        }

        return a * 0.5;

    },

    triangulate: ( function () {

        /**
         * This code is a quick port of code written in C++ which was submitted to
         * flipcode.com by John W. Ratcliff  // July 22, 2000
         * See original code and more information here:
         * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
         *
         * ported to actionscript by Zevan Rosser
         * www.actionsnippet.com
         *
         * ported to javascript by Joshua Koo
         * http://www.lab4games.net/zz85/blog
         *
         */

        function snip( contour, u, v, w, n, verts ) {

            var p;
            var ax, ay, bx, by;
            var cx, cy, px, py;

            ax = contour[ verts[ u ] ].x;
            ay = contour[ verts[ u ] ].y;

            bx = contour[ verts[ v ] ].x;
            by = contour[ verts[ v ] ].y;

            cx = contour[ verts[ w ] ].x;
            cy = contour[ verts[ w ] ].y;

            if ( Number.EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false;

            var aX, aY, bX, bY, cX, cY;
            var apx, apy, bpx, bpy, cpx, cpy;
            var cCROSSap, bCROSScp, aCROSSbp;

            aX = cx - bx;  aY = cy - by;
            bX = ax - cx;  bY = ay - cy;
            cX = bx - ax;  cY = by - ay;

            for ( p = 0; p < n; p ++ ) {

                px = contour[ verts[ p ] ].x;
                py = contour[ verts[ p ] ].y;

                if ( ( ( px === ax ) && ( py === ay ) ) ||
                    ( ( px === bx ) && ( py === by ) ) ||
                    ( ( px === cx ) && ( py === cy ) ) )	continue;

                apx = px - ax;  apy = py - ay;
                bpx = px - bx;  bpy = py - by;
                cpx = px - cx;  cpy = py - cy;

                // see if p is inside triangle abc

                aCROSSbp = aX * bpy - aY * bpx;
                cCROSSap = cX * apy - cY * apx;
                bCROSScp = bX * cpy - bY * cpx;

                if ( ( aCROSSbp >= - Number.EPSILON ) && ( bCROSScp >= - Number.EPSILON ) && ( cCROSSap >= - Number.EPSILON ) ) return false;

            }

            return true;

        }

        // takes in an contour array and returns

        return function ( contour, indices ) {

            var n = contour.length;

            if ( n < 3 ) return null;

            var result = [],
                verts = [],
                vertIndices = [];

            /* we want a counter-clockwise polygon in verts */

            var u, v, w;

            if ( THREE.ShapeUtils.area( contour ) > 0.0 ) {

                for ( v = 0; v < n; v ++ ) verts[ v ] = v;

            } else {

                for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v;

            }

            var nv = n;

            /*  remove nv - 2 vertices, creating 1 triangle every time */

            var count = 2 * nv;   /* error detection */

            for ( v = nv - 1; nv > 2; ) {

                /* if we loop, it is probably a non-simple polygon */

                if ( ( count -- ) <= 0 ) {

                    //** Triangulate: ERROR - probable bad polygon!

                    //throw ( "Warning, unable to triangulate polygon!" );
                    //return null;
                    // Sometimes warning is fine, especially polygons are triangulated in reverse.
                    console.warn( 'THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()' );

                    if ( indices ) return vertIndices;
                    return result;

                }

                /* three consecutive vertices in current polygon, <u,v,w> */

                u = v; 	 	if ( nv <= u ) u = 0;     /* previous */
                v = u + 1;  if ( nv <= v ) v = 0;     /* new v    */
                w = v + 1;  if ( nv <= w ) w = 0;     /* next     */

                if ( snip( contour, u, v, w, nv, verts ) ) {

                    var a, b, c, s, t;

                    /* true names of the vertices */

                    a = verts[ u ];
                    b = verts[ v ];
                    c = verts[ w ];

                    /* output Triangle */

                    result.push( [ contour[ a ],
                        contour[ b ],
                        contour[ c ] ] );


                    vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );

                    /* remove v from the remaining polygon */

                    for ( s = v, t = v + 1; t < nv; s ++, t ++ ) {

                        verts[ s ] = verts[ t ];

                    }

                    nv --;

                    /* reset error detection counter */

                    count = 2 * nv;

                }

            }

            if ( indices ) return vertIndices;
            return result;

        }

    } )(),

    triangulateShape: function ( contour, holes ) {

        function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) {

            // inOtherPt needs to be collinear to the inSegment
            if ( inSegPt1.x !== inSegPt2.x ) {

                if ( inSegPt1.x < inSegPt2.x ) {

                    return	( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) );

                } else {

                    return	( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) );

                }

            } else {

                if ( inSegPt1.y < inSegPt2.y ) {

                    return	( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) );

                } else {

                    return	( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) );

                }

            }

        }

        function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) {

            var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x,   seg1dy = inSeg1Pt2.y - inSeg1Pt1.y;
            var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x,   seg2dy = inSeg2Pt2.y - inSeg2Pt1.y;

            var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x;
            var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y;

            var limit		= seg1dy * seg2dx - seg1dx * seg2dy;
            var perpSeg1	= seg1dy * seg1seg2dx - seg1dx * seg1seg2dy;

            if ( Math.abs( limit ) > Number.EPSILON ) {

                // not parallel

                var perpSeg2;
                if ( limit > 0 ) {

                    if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) 		return [];
                    perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
                    if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) 		return [];

                } else {

                    if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) 		return [];
                    perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
                    if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) 		return [];

                }

                // i.e. to reduce rounding errors
                // intersection at endpoint of segment#1?
                if ( perpSeg2 === 0 ) {

                    if ( ( inExcludeAdjacentSegs ) &&
                        ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) )		return [];
                    return [ inSeg1Pt1 ];

                }
                if ( perpSeg2 === limit ) {

                    if ( ( inExcludeAdjacentSegs ) &&
                        ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) )		return [];
                    return [ inSeg1Pt2 ];

                }
                // intersection at endpoint of segment#2?
                if ( perpSeg1 === 0 )		return [ inSeg2Pt1 ];
                if ( perpSeg1 === limit )	return [ inSeg2Pt2 ];

                // return real intersection point
                var factorSeg1 = perpSeg2 / limit;
                return	[ { x: inSeg1Pt1.x + factorSeg1 * seg1dx,
                    y: inSeg1Pt1.y + factorSeg1 * seg1dy } ];

            } else {

                // parallel or collinear
                if ( ( perpSeg1 !== 0 ) ||
                    ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) 			return [];

                // they are collinear or degenerate
                var seg1Pt = ( ( seg1dx === 0 ) && ( seg1dy === 0 ) );	// segment1 is just a point?
                var seg2Pt = ( ( seg2dx === 0 ) && ( seg2dy === 0 ) );	// segment2 is just a point?
                // both segments are points
                if ( seg1Pt && seg2Pt ) {

                    if ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) ||
                        ( inSeg1Pt1.y !== inSeg2Pt1.y ) )		return [];	// they are distinct  points
                    return [ inSeg1Pt1 ];                 						// they are the same point

                }
                // segment#1  is a single point
                if ( seg1Pt ) {

                    if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) )		return [];		// but not in segment#2
                    return [ inSeg1Pt1 ];

                }
                // segment#2  is a single point
                if ( seg2Pt ) {

                    if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) )		return [];		// but not in segment#1
                    return [ inSeg2Pt1 ];

                }

                // they are collinear segments, which might overlap
                var seg1min, seg1max, seg1minVal, seg1maxVal;
                var seg2min, seg2max, seg2minVal, seg2maxVal;
                if ( seg1dx !== 0 ) {

                    // the segments are NOT on a vertical line
                    if ( inSeg1Pt1.x < inSeg1Pt2.x ) {

                        seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x;
                        seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x;

                    } else {

                        seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x;
                        seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x;

                    }
                    if ( inSeg2Pt1.x < inSeg2Pt2.x ) {

                        seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x;
                        seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x;

                    } else {

                        seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x;
                        seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x;

                    }

                } else {

                    // the segments are on a vertical line
                    if ( inSeg1Pt1.y < inSeg1Pt2.y ) {

                        seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y;
                        seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y;

                    } else {

                        seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y;
                        seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y;

                    }
                    if ( inSeg2Pt1.y < inSeg2Pt2.y ) {

                        seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y;
                        seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y;

                    } else {

                        seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y;
                        seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y;

                    }

                }
                if ( seg1minVal <= seg2minVal ) {

                    if ( seg1maxVal <  seg2minVal )	return [];
                    if ( seg1maxVal === seg2minVal )	{

                        if ( inExcludeAdjacentSegs )		return [];
                        return [ seg2min ];

                    }
                    if ( seg1maxVal <= seg2maxVal )	return [ seg2min, seg1max ];
                    return	[ seg2min, seg2max ];

                } else {

                    if ( seg1minVal >  seg2maxVal )	return [];
                    if ( seg1minVal === seg2maxVal )	{

                        if ( inExcludeAdjacentSegs )		return [];
                        return [ seg1min ];

                    }
                    if ( seg1maxVal <= seg2maxVal )	return [ seg1min, seg1max ];
                    return	[ seg1min, seg2max ];

                }

            }

        }

        function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) {

            // The order of legs is important

            // translation of all points, so that Vertex is at (0,0)
            var legFromPtX	= inLegFromPt.x - inVertex.x,  legFromPtY	= inLegFromPt.y - inVertex.y;
            var legToPtX	= inLegToPt.x	- inVertex.x,  legToPtY		= inLegToPt.y	- inVertex.y;
            var otherPtX	= inOtherPt.x	- inVertex.x,  otherPtY		= inOtherPt.y	- inVertex.y;

            // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg.
            var from2toAngle	= legFromPtX * legToPtY - legFromPtY * legToPtX;
            var from2otherAngle	= legFromPtX * otherPtY - legFromPtY * otherPtX;

            if ( Math.abs( from2toAngle ) > Number.EPSILON ) {

                // angle != 180 deg.

                var other2toAngle		= otherPtX * legToPtY - otherPtY * legToPtX;
                // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle );

                if ( from2toAngle > 0 ) {

                    // main angle < 180 deg.
                    return	( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) );

                } else {

                    // main angle > 180 deg.
                    return	( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) );

                }

            } else {

                // angle == 180 deg.
                // console.log( "from2to: 180 deg., from2other: " + from2otherAngle  );
                return	( from2otherAngle > 0 );

            }

        }


        function removeHoles( contour, holes ) {

            var shape = contour.concat(); // work on this shape
            var hole;

            function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) {

                // Check if hole point lies within angle around shape point
                var lastShapeIdx = shape.length - 1;

                var prevShapeIdx = inShapeIdx - 1;
                if ( prevShapeIdx < 0 )			prevShapeIdx = lastShapeIdx;

                var nextShapeIdx = inShapeIdx + 1;
                if ( nextShapeIdx > lastShapeIdx )	nextShapeIdx = 0;

                var insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] );
                if ( ! insideAngle ) {

                    // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y );
                    return	false;

                }

                // Check if shape point lies within angle around hole point
                var lastHoleIdx = hole.length - 1;

                var prevHoleIdx = inHoleIdx - 1;
                if ( prevHoleIdx < 0 )			prevHoleIdx = lastHoleIdx;

                var nextHoleIdx = inHoleIdx + 1;
                if ( nextHoleIdx > lastHoleIdx )	nextHoleIdx = 0;

                insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] );
                if ( ! insideAngle ) {

                    // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y );
                    return	false;

                }

                return	true;

            }

            function intersectsShapeEdge( inShapePt, inHolePt ) {

                // checks for intersections with shape edges
                var sIdx, nextIdx, intersection;
                for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) {

                    nextIdx = sIdx + 1; nextIdx %= shape.length;
                    intersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true );
                    if ( intersection.length > 0 )		return	true;

                }

                return	false;

            }

            var indepHoles = [];

            function intersectsHoleEdge( inShapePt, inHolePt ) {

                // checks for intersections with hole edges
                var ihIdx, chkHole,
                    hIdx, nextIdx, intersection;
                for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) {

                    chkHole = holes[ indepHoles[ ihIdx ]];
                    for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) {

                        nextIdx = hIdx + 1; nextIdx %= chkHole.length;
                        intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true );
                        if ( intersection.length > 0 )		return	true;

                    }

                }
                return	false;

            }

            var holeIndex, shapeIndex,
                shapePt, holePt,
                holeIdx, cutKey, failedCuts = [],
                tmpShape1, tmpShape2,
                tmpHole1, tmpHole2;

            for ( var h = 0, hl = holes.length; h < hl; h ++ ) {

                indepHoles.push( h );

            }

            var minShapeIndex = 0;
            var counter = indepHoles.length * 2;
            while ( indepHoles.length > 0 ) {

                counter --;
                if ( counter < 0 ) {

                    console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" );
                    break;

                }

                // search for shape-vertex and hole-vertex,
                // which can be connected without intersections
                for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) {

                    shapePt = shape[ shapeIndex ];
                    holeIndex	= - 1;

                    // search for hole which can be reached without intersections
                    for ( var h = 0; h < indepHoles.length; h ++ ) {

                        holeIdx = indepHoles[ h ];

                        // prevent multiple checks
                        cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx;
                        if ( failedCuts[ cutKey ] !== undefined )			continue;

                        hole = holes[ holeIdx ];
                        for ( var h2 = 0; h2 < hole.length; h2 ++ ) {

                            holePt = hole[ h2 ];
                            if ( ! isCutLineInsideAngles( shapeIndex, h2 ) )		continue;
                            if ( intersectsShapeEdge( shapePt, holePt ) )		continue;
                            if ( intersectsHoleEdge( shapePt, holePt ) )		continue;

                            holeIndex = h2;
                            indepHoles.splice( h, 1 );

                            tmpShape1 = shape.slice( 0, shapeIndex + 1 );
                            tmpShape2 = shape.slice( shapeIndex );
                            tmpHole1 = hole.slice( holeIndex );
                            tmpHole2 = hole.slice( 0, holeIndex + 1 );

                            shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );

                            minShapeIndex = shapeIndex;

                            // Debug only, to show the selected cuts
                            // glob_CutLines.push( [ shapePt, holePt ] );

                            break;

                        }
                        if ( holeIndex >= 0 )	break;		// hole-vertex found

                        failedCuts[ cutKey ] = true;			// remember failure

                    }
                    if ( holeIndex >= 0 )	break;		// hole-vertex found

                }

            }

            return shape; 			/* shape with no holes */

        }


        var i, il, f, face,
            key, index,
            allPointsMap = {};

        // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.

        var allpoints = contour.concat();

        for ( var h = 0, hl = holes.length; h < hl; h ++ ) {

            Array.prototype.push.apply( allpoints, holes[ h ] );

        }

        //console.log( "allpoints",allpoints, allpoints.length );

        // prepare all points map

        for ( i = 0, il = allpoints.length; i < il; i ++ ) {

            key = allpoints[ i ].x + ":" + allpoints[ i ].y;

            if ( allPointsMap[ key ] !== undefined ) {

                console.warn( "THREE.Shape: Duplicate point", key );

            }

            allPointsMap[ key ] = i;

        }

        // remove holes by cutting paths to holes and adding them to the shape
        var shapeWithoutHoles = removeHoles( contour, holes );

        var triangles = THREE.ShapeUtils.triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape
        //console.log( "triangles",triangles, triangles.length );

        // check all face vertices against all points map

        for ( i = 0, il = triangles.length; i < il; i ++ ) {

            face = triangles[ i ];

            for ( f = 0; f < 3; f ++ ) {

                key = face[ f ].x + ":" + face[ f ].y;

                index = allPointsMap[ key ];

                if ( index !== undefined ) {

                    face[ f ] = index;

                }

            }

        }

        return triangles.concat();

    },

    isClockWise: function ( pts ) {

        return THREE.ShapeUtils.area( pts ) < 0;

    },

    // Bezier Curves formulas obtained from
    // http://en.wikipedia.org/wiki/B%C3%A9zier_curve

    // Quad Bezier Functions

    b2: ( function () {

        function b2p0( t, p ) {

            var k = 1 - t;
            return k * k * p;

        }

        function b2p1( t, p ) {

            return 2 * ( 1 - t ) * t * p;

        }

        function b2p2( t, p ) {

            return t * t * p;

        }

        return function ( t, p0, p1, p2 ) {

            return b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 );

        };

    } )(),

    // Cubic Bezier Functions

    b3: ( function () {

        function b3p0( t, p ) {

            var k = 1 - t;
            return k * k * k * p;

        }

        function b3p1( t, p ) {

            var k = 1 - t;
            return 3 * k * k * t * p;

        }

        function b3p2( t, p ) {

            var k = 1 - t;
            return 3 * k * t * t * p;

        }

        function b3p3( t, p ) {

            return t * t * t * p;

        }

        return function ( t, p0, p1, p2, p3 ) {

            return b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 );

        };

    } )()

};

// File:src/extras/audio/Audio.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.Audio = function ( listener ) {

    THREE.Object3D.call( this );

    this.type = 'Audio';

    this.context = listener.context;
    this.source = this.context.createBufferSource();
    this.source.onended = this.onEnded.bind( this );

    this.gain = this.context.createGain();
    this.gain.connect( this.context.destination );

    this.panner = this.context.createPanner();
    this.panner.connect( this.gain );

    this.autoplay = false;

    this.startTime = 0;
    this.playbackRate = 1;
    this.isPlaying = false;

};

THREE.Audio.prototype = Object.create( THREE.Object3D.prototype );
THREE.Audio.prototype.constructor = THREE.Audio;

THREE.Audio.prototype.load = function ( file ) {

    var scope = this;

    var request = new XMLHttpRequest();
    request.open( 'GET', file, true );
    request.responseType = 'arraybuffer';
    request.onload = function ( e ) {

        scope.context.decodeAudioData( this.response, function ( buffer ) {

            scope.source.buffer = buffer;

            if ( scope.autoplay ) scope.play();

        } );

    };
    request.send();

    return this;

};

THREE.Audio.prototype.play = function () {

    if ( this.isPlaying === true ) {

        console.warn( 'THREE.Audio: Audio is already playing.' );
        return;

    }

    var source = this.context.createBufferSource();

    source.buffer = this.source.buffer;
    source.loop = this.source.loop;
    source.onended = this.source.onended;
    source.start( 0, this.startTime );
    source.playbackRate.value = this.playbackRate;

    this.isPlaying = true;

    this.source = source;

    this.connect();

};

THREE.Audio.prototype.pause = function () {

    this.source.stop();
    this.startTime = this.context.currentTime;

};

THREE.Audio.prototype.stop = function () {

    this.source.stop();
    this.startTime = 0;

};

THREE.Audio.prototype.connect = function () {

    if ( this.filter !== undefined ) {

        this.source.connect( this.filter );
        this.filter.connect( this.panner );

    } else {

        this.source.connect( this.panner );

    }

};

THREE.Audio.prototype.disconnect = function () {

    if ( this.filter !== undefined ) {

        this.source.disconnect( this.filter );
        this.filter.disconnect( this.panner );

    } else {

        this.source.disconnect( this.panner );

    }

};

THREE.Audio.prototype.setFilter = function ( value ) {

    if ( this.isPlaying === true ) {

        this.disconnect();
        this.filter = value;
        this.connect();

    } else {

        this.filter = value;

    }

};

THREE.Audio.prototype.getFilter = function () {

    return this.filter;

};

THREE.Audio.prototype.setPlaybackRate = function ( value ) {

    this.playbackRate = value;

    if ( this.isPlaying === true ) {

        this.source.playbackRate.value = this.playbackRate;

    }

};

THREE.Audio.prototype.getPlaybackRate = function () {

    return this.playbackRate;

};

THREE.Audio.prototype.onEnded = function() {

    this.isPlaying = false;

};

THREE.Audio.prototype.setLoop = function ( value ) {

    this.source.loop = value;

};

THREE.Audio.prototype.getLoop = function () {

    return this.source.loop;

};

THREE.Audio.prototype.setRefDistance = function ( value ) {

    this.panner.refDistance = value;

};

THREE.Audio.prototype.getRefDistance = function () {

    return this.panner.refDistance;

};

THREE.Audio.prototype.setRolloffFactor = function ( value ) {

    this.panner.rolloffFactor = value;

};

THREE.Audio.prototype.getRolloffFactor = function () {

    return this.panner.rolloffFactor;

};

THREE.Audio.prototype.setVolume = function ( value ) {

    this.gain.gain.value = value;

};

THREE.Audio.prototype.getVolume = function () {

    return this.gain.gain.value;

};

THREE.Audio.prototype.updateMatrixWorld = ( function () {

    var position = new THREE.Vector3();

    return function updateMatrixWorld( force ) {

        THREE.Object3D.prototype.updateMatrixWorld.call( this, force );

        position.setFromMatrixPosition( this.matrixWorld );

        this.panner.setPosition( position.x, position.y, position.z );

    };

} )();

// File:src/extras/audio/AudioListener.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.AudioListener = function () {

    THREE.Object3D.call( this );

    this.type = 'AudioListener';

    this.context = new ( window.AudioContext || window.webkitAudioContext )();

};

THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype );
THREE.AudioListener.prototype.constructor = THREE.AudioListener;

THREE.AudioListener.prototype.updateMatrixWorld = ( function () {

    var position = new THREE.Vector3();
    var quaternion = new THREE.Quaternion();
    var scale = new THREE.Vector3();

    var orientation = new THREE.Vector3();

    return function updateMatrixWorld( force ) {

        THREE.Object3D.prototype.updateMatrixWorld.call( this, force );

        var listener = this.context.listener;
        var up = this.up;

        this.matrixWorld.decompose( position, quaternion, scale );

        orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion );

        listener.setPosition( position.x, position.y, position.z );
        listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z );

    };

} )();

// File:src/extras/core/Curve.js

/**
 * @author zz85 / http://www.lab4games.net/zz85/blog
 * Extensible curve object
 *
 * Some common of Curve methods
 * .getPoint(t), getTangent(t)
 * .getPointAt(u), getTagentAt(u)
 * .getPoints(), .getSpacedPoints()
 * .getLength()
 * .updateArcLengths()
 *
 * This following classes subclasses THREE.Curve:
 *
 * -- 2d classes --
 * THREE.LineCurve
 * THREE.QuadraticBezierCurve
 * THREE.CubicBezierCurve
 * THREE.SplineCurve
 * THREE.ArcCurve
 * THREE.EllipseCurve
 *
 * -- 3d classes --
 * THREE.LineCurve3
 * THREE.QuadraticBezierCurve3
 * THREE.CubicBezierCurve3
 * THREE.SplineCurve3
 * THREE.ClosedSplineCurve3
 *
 * A series of curves can be represented as a THREE.CurvePath
 *
 **/

/**************************************************************
 *	Abstract Curve base class
 **************************************************************/

THREE.Curve = function () {

};

THREE.Curve.prototype = {

    constructor: THREE.Curve,

    // Virtual base class method to overwrite and implement in subclasses
    //	- t [0 .. 1]

    getPoint: function ( t ) {

        console.warn( "THREE.Curve: Warning, getPoint() not implemented!" );
        return null;

    },

    // Get point at relative position in curve according to arc length
    // - u [0 .. 1]

    getPointAt: function ( u ) {

        var t = this.getUtoTmapping( u );
        return this.getPoint( t );

    },

    // Get sequence of points using getPoint( t )

    getPoints: function ( divisions ) {

        if ( ! divisions ) divisions = 5;

        var d, pts = [];

        for ( d = 0; d <= divisions; d ++ ) {

            pts.push( this.getPoint( d / divisions ) );

        }

        return pts;

    },

    // Get sequence of points using getPointAt( u )

    getSpacedPoints: function ( divisions ) {

        if ( ! divisions ) divisions = 5;

        var d, pts = [];

        for ( d = 0; d <= divisions; d ++ ) {

            pts.push( this.getPointAt( d / divisions ) );

        }

        return pts;

    },

    // Get total curve arc length

    getLength: function () {

        var lengths = this.getLengths();
        return lengths[ lengths.length - 1 ];

    },

    // Get list of cumulative segment lengths

    getLengths: function ( divisions ) {

        if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200;

        if ( this.cacheArcLengths
            && ( this.cacheArcLengths.length === divisions + 1 )
            && ! this.needsUpdate ) {

            //console.log( "cached", this.cacheArcLengths );
            return this.cacheArcLengths;

        }

        this.needsUpdate = false;

        var cache = [];
        var current, last = this.getPoint( 0 );
        var p, sum = 0;

        cache.push( 0 );

        for ( p = 1; p <= divisions; p ++ ) {

            current = this.getPoint ( p / divisions );
            sum += current.distanceTo( last );
            cache.push( sum );
            last = current;

        }

        this.cacheArcLengths = cache;

        return cache; // { sums: cache, sum:sum }; Sum is in the last element.

    },

    updateArcLengths: function() {

        this.needsUpdate = true;
        this.getLengths();

    },

    // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant

    getUtoTmapping: function ( u, distance ) {

        var arcLengths = this.getLengths();

        var i = 0, il = arcLengths.length;

        var targetArcLength; // The targeted u distance value to get

        if ( distance ) {

            targetArcLength = distance;

        } else {

            targetArcLength = u * arcLengths[ il - 1 ];

        }

        //var time = Date.now();

        // binary search for the index with largest value smaller than target u distance

        var low = 0, high = il - 1, comparison;

        while ( low <= high ) {

            i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats

            comparison = arcLengths[ i ] - targetArcLength;

            if ( comparison < 0 ) {

                low = i + 1;

            } else if ( comparison > 0 ) {

                high = i - 1;

            } else {

                high = i;
                break;

                // DONE

            }

        }

        i = high;

        //console.log('b' , i, low, high, Date.now()- time);

        if ( arcLengths[ i ] === targetArcLength ) {

            var t = i / ( il - 1 );
            return t;

        }

        // we could get finer grain at lengths, or use simple interpolation between two points

        var lengthBefore = arcLengths[ i ];
        var lengthAfter = arcLengths[ i + 1 ];

        var segmentLength = lengthAfter - lengthBefore;

        // determine where we are between the 'before' and 'after' points

        var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;

        // add that fractional amount to t

        var t = ( i + segmentFraction ) / ( il - 1 );

        return t;

    },

    // Returns a unit vector tangent at t
    // In case any sub curve does not implement its tangent derivation,
    // 2 points a small delta apart will be used to find its gradient
    // which seems to give a reasonable approximation

    getTangent: function( t ) {

        var delta = 0.0001;
        var t1 = t - delta;
        var t2 = t + delta;

        // Capping in case of danger

        if ( t1 < 0 ) t1 = 0;
        if ( t2 > 1 ) t2 = 1;

        var pt1 = this.getPoint( t1 );
        var pt2 = this.getPoint( t2 );

        var vec = pt2.clone().sub( pt1 );
        return vec.normalize();

    },

    getTangentAt: function ( u ) {

        var t = this.getUtoTmapping( u );
        return this.getTangent( t );

    }

}

THREE.Curve.Utils = THREE.CurveUtils; // backwards compatibility

// TODO: Transformation for Curves?

/**************************************************************
 *	3D Curves
 **************************************************************/

// A Factory method for creating new curve subclasses

THREE.Curve.create = function ( constructor, getPointFunc ) {

    constructor.prototype = Object.create( THREE.Curve.prototype );
    constructor.prototype.constructor = constructor;
    constructor.prototype.getPoint = getPointFunc;

    return constructor;

};

// File:src/extras/core/CurvePath.js

/**
 * @author zz85 / http://www.lab4games.net/zz85/blog
 *
 **/

/**************************************************************
 *	Curved Path - a curve path is simply a array of connected
 *  curves, but retains the api of a curve
 **************************************************************/

THREE.CurvePath = function () {

    this.curves = [];

    this.autoClose = false; // Automatically closes the path

};

THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype );
THREE.CurvePath.prototype.constructor = THREE.CurvePath;

THREE.CurvePath.prototype.add = function ( curve ) {

    this.curves.push( curve );

};

/*
THREE.CurvePath.prototype.checkConnection = function() {
	// TODO
	// If the ending of curve is not connected to the starting
	// or the next curve, then, this is not a real path
};
*/

THREE.CurvePath.prototype.closePath = function() {

    // TODO Test
    // and verify for vector3 (needs to implement equals)
    // Add a line curve if start and end of lines are not connected
    var startPoint = this.curves[ 0 ].getPoint( 0 );
    var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );

    if ( ! startPoint.equals( endPoint ) ) {

        this.curves.push( new THREE.LineCurve( endPoint, startPoint ) );

    }

};

// To get accurate point with reference to
// entire path distance at time t,
// following has to be done:

// 1. Length of each sub path have to be known
// 2. Locate and identify type of curve
// 3. Get t for the curve
// 4. Return curve.getPointAt(t')

THREE.CurvePath.prototype.getPoint = function( t ) {

    var d = t * this.getLength();
    var curveLengths = this.getCurveLengths();
    var i = 0;

    // To think about boundaries points.

    while ( i < curveLengths.length ) {

        if ( curveLengths[ i ] >= d ) {

            var diff = curveLengths[ i ] - d;
            var curve = this.curves[ i ];

            var u = 1 - diff / curve.getLength();

            return curve.getPointAt( u );

        }

        i ++;

    }

    return null;

    // loop where sum != 0, sum > d , sum+1 <d

};

/*
THREE.CurvePath.prototype.getTangent = function( t ) {
};
*/

// We cannot use the default THREE.Curve getPoint() with getLength() because in
// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath
// getPoint() depends on getLength

THREE.CurvePath.prototype.getLength = function() {

    var lens = this.getCurveLengths();
    return lens[ lens.length - 1 ];

};

// Compute lengths and cache them
// We cannot overwrite getLengths() because UtoT mapping uses it.

THREE.CurvePath.prototype.getCurveLengths = function() {

    // We use cache values if curves and cache array are same length

    if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) {

        return this.cacheLengths;

    }

    // Get length of sub-curve
    // Push sums into cached array

    var lengths = [], sums = 0;

    for ( var i = 0, l = this.curves.length; i < l; i ++ ) {

        sums += this.curves[ i ].getLength();
        lengths.push( sums );

    }

    this.cacheLengths = lengths;

    return lengths;

};



/**************************************************************
 *	Create Geometries Helpers
 **************************************************************/

/// Generate geometry from path points (for Line or Points objects)

THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) {

    var pts = this.getPoints( divisions, true );
    return this.createGeometry( pts );

};

// Generate geometry from equidistant sampling along the path

THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) {

    var pts = this.getSpacedPoints( divisions, true );
    return this.createGeometry( pts );

};

THREE.CurvePath.prototype.createGeometry = function( points ) {

    var geometry = new THREE.Geometry();

    for ( var i = 0, l = points.length; i < l; i ++ ) {

        var point = points[ i ];
        geometry.vertices.push( new THREE.Vector3( point.x, point.y, point.z || 0 ) );

    }

    return geometry;

};

// File:src/extras/core/Path.js

/**
 * @author zz85 / http://www.lab4games.net/zz85/blog
 * Creates free form 2d path using series of points, lines or curves.
 *
 **/

THREE.Path = function ( points ) {

    THREE.CurvePath.call( this );

    this.actions = [];

    if ( points ) {

        this.fromPoints( points );

    }

};

THREE.Path.prototype = Object.create( THREE.CurvePath.prototype );
THREE.Path.prototype.constructor = THREE.Path;

// TODO Clean up PATH API

// Create path using straight lines to connect all points
// - vectors: array of Vector2

THREE.Path.prototype.fromPoints = function ( vectors ) {

    this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );

    for ( var i = 1, l = vectors.length; i < l; i ++ ) {

        this.lineTo( vectors[ i ].x, vectors[ i ].y );

    }

};

// startPath() endPath()?

THREE.Path.prototype.moveTo = function ( x, y ) {

    this.actions.push( { action: 'moveTo', args: [ x, y ] } );

};

THREE.Path.prototype.lineTo = function ( x, y ) {

    var lastargs = this.actions[ this.actions.length - 1 ].args;

    var x0 = lastargs[ lastargs.length - 2 ];
    var y0 = lastargs[ lastargs.length - 1 ];

    var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) );
    this.curves.push( curve );

    this.actions.push( { action: 'lineTo', args: [ x, y ] } );

};

THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {

    var lastargs = this.actions[ this.actions.length - 1 ].args;

    var x0 = lastargs[ lastargs.length - 2 ];
    var y0 = lastargs[ lastargs.length - 1 ];

    var curve = new THREE.QuadraticBezierCurve(
        new THREE.Vector2( x0, y0 ),
        new THREE.Vector2( aCPx, aCPy ),
        new THREE.Vector2( aX, aY )
    );

    this.curves.push( curve );

    this.actions.push( { action: 'quadraticCurveTo', args: [ aCPx, aCPy, aX, aY ] } );

};

THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {

    var lastargs = this.actions[ this.actions.length - 1 ].args;

    var x0 = lastargs[ lastargs.length - 2 ];
    var y0 = lastargs[ lastargs.length - 1 ];

    var curve = new THREE.CubicBezierCurve(
        new THREE.Vector2( x0, y0 ),
        new THREE.Vector2( aCP1x, aCP1y ),
        new THREE.Vector2( aCP2x, aCP2y ),
        new THREE.Vector2( aX, aY )
    );

    this.curves.push( curve );

    this.actions.push( { action: 'bezierCurveTo', args: [ aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ] } );

};

THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) {

    var args = Array.prototype.slice.call( arguments );

    var lastargs = this.actions[ this.actions.length - 1 ].args;

    var x0 = lastargs[ lastargs.length - 2 ];
    var y0 = lastargs[ lastargs.length - 1 ];

    var npts = [ new THREE.Vector2( x0, y0 ) ];
    Array.prototype.push.apply( npts, pts );

    var curve = new THREE.SplineCurve( npts );
    this.curves.push( curve );

    this.actions.push( { action: 'splineThru', args: args } );

};

// FUTURE: Change the API or follow canvas API?

THREE.Path.prototype.arc = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {

    var lastargs = this.actions[ this.actions.length - 1 ].args;
    var x0 = lastargs[ lastargs.length - 2 ];
    var y0 = lastargs[ lastargs.length - 1 ];

    this.absarc( aX + x0, aY + y0, aRadius,
        aStartAngle, aEndAngle, aClockwise );

};

THREE.Path.prototype.absarc = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {

    this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );

};

THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {

    var lastargs = this.actions[ this.actions.length - 1 ].args;
    var x0 = lastargs[ lastargs.length - 2 ];
    var y0 = lastargs[ lastargs.length - 1 ];

    this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );

};


THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {

    var args = [
        aX, aY,
        xRadius, yRadius,
        aStartAngle, aEndAngle,
        aClockwise,
        aRotation || 0 // aRotation is optional.
    ];

    var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );
    this.curves.push( curve );

    var lastPoint = curve.getPoint( 1 );
    args.push( lastPoint.x );
    args.push( lastPoint.y );

    this.actions.push( { action: 'ellipse', args: args } );

};

THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) {

    if ( ! divisions ) divisions = 40;

    var points = [];

    for ( var i = 0; i < divisions; i ++ ) {

        points.push( this.getPoint( i / divisions ) );

        //if ( !this.getPoint( i / divisions ) ) throw "DIE";

    }

    // if ( closedPath ) {
    //
    // 	points.push( points[ 0 ] );
    //
    // }

    return points;

};

/* Return an array of vectors based on contour of the path */

THREE.Path.prototype.getPoints = function( divisions, closedPath ) {

    divisions = divisions || 12;

    var b2 = THREE.ShapeUtils.b2;
    var b3 = THREE.ShapeUtils.b3;

    var points = [];

    var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
        laste, tx, ty;

    for ( var i = 0, l = this.actions.length; i < l; i ++ ) {

        var item = this.actions[ i ];

        var action = item.action;
        var args = item.args;

        switch ( action ) {

            case 'moveTo':

                points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );

                break;

            case 'lineTo':

                points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );

                break;

            case 'quadraticCurveTo':

                cpx  = args[ 2 ];
                cpy  = args[ 3 ];

                cpx1 = args[ 0 ];
                cpy1 = args[ 1 ];

                if ( points.length > 0 ) {

                    laste = points[ points.length - 1 ];

                    cpx0 = laste.x;
                    cpy0 = laste.y;

                } else {

                    laste = this.actions[ i - 1 ].args;

                    cpx0 = laste[ laste.length - 2 ];
                    cpy0 = laste[ laste.length - 1 ];

                }

                for ( var j = 1; j <= divisions; j ++ ) {

                    var t = j / divisions;

                    tx = b2( t, cpx0, cpx1, cpx );
                    ty = b2( t, cpy0, cpy1, cpy );

                    points.push( new THREE.Vector2( tx, ty ) );

                }

                break;

            case 'bezierCurveTo':

                cpx  = args[ 4 ];
                cpy  = args[ 5 ];

                cpx1 = args[ 0 ];
                cpy1 = args[ 1 ];

                cpx2 = args[ 2 ];
                cpy2 = args[ 3 ];

                if ( points.length > 0 ) {

                    laste = points[ points.length - 1 ];

                    cpx0 = laste.x;
                    cpy0 = laste.y;

                } else {

                    laste = this.actions[ i - 1 ].args;

                    cpx0 = laste[ laste.length - 2 ];
                    cpy0 = laste[ laste.length - 1 ];

                }


                for ( var j = 1; j <= divisions; j ++ ) {

                    var t = j / divisions;

                    tx = b3( t, cpx0, cpx1, cpx2, cpx );
                    ty = b3( t, cpy0, cpy1, cpy2, cpy );

                    points.push( new THREE.Vector2( tx, ty ) );

                }

                break;

            case 'splineThru':

                laste = this.actions[ i - 1 ].args;

                var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
                var spts = [ last ];

                var n = divisions * args[ 0 ].length;

                spts = spts.concat( args[ 0 ] );

                var spline = new THREE.SplineCurve( spts );

                for ( var j = 1; j <= n; j ++ ) {

                    points.push( spline.getPointAt( j / n ) );

                }

                break;

            case 'arc':

                var aX = args[ 0 ], aY = args[ 1 ],
                    aRadius = args[ 2 ],
                    aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
                    aClockwise = !! args[ 5 ];

                var deltaAngle = aEndAngle - aStartAngle;
                var angle;
                var tdivisions = divisions * 2;

                for ( var j = 1; j <= tdivisions; j ++ ) {

                    var t = j / tdivisions;

                    if ( ! aClockwise ) {

                        t = 1 - t;

                    }

                    angle = aStartAngle + t * deltaAngle;

                    tx = aX + aRadius * Math.cos( angle );
                    ty = aY + aRadius * Math.sin( angle );

                    //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);

                    points.push( new THREE.Vector2( tx, ty ) );

                }

                //console.log(points);

                break;

            case 'ellipse':

                var aX = args[ 0 ], aY = args[ 1 ],
                    xRadius = args[ 2 ],
                    yRadius = args[ 3 ],
                    aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
                    aClockwise = !! args[ 6 ],
                    aRotation = args[ 7 ];


                var deltaAngle = aEndAngle - aStartAngle;
                var angle;
                var tdivisions = divisions * 2;

                var cos, sin;
                if ( aRotation !== 0 ) {

                    cos = Math.cos( aRotation );
                    sin = Math.sin( aRotation );

                }

                for ( var j = 1; j <= tdivisions; j ++ ) {

                    var t = j / tdivisions;

                    if ( ! aClockwise ) {

                        t = 1 - t;

                    }

                    angle = aStartAngle + t * deltaAngle;

                    tx = aX + xRadius * Math.cos( angle );
                    ty = aY + yRadius * Math.sin( angle );

                    if ( aRotation !== 0 ) {

                        var x = tx, y = ty;

                        // Rotate the point about the center of the ellipse.
                        tx = ( x - aX ) * cos - ( y - aY ) * sin + aX;
                        ty = ( x - aX ) * sin + ( y - aY ) * cos + aY;

                    }

                    //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);

                    points.push( new THREE.Vector2( tx, ty ) );

                }

                //console.log(points);

                break;

        } // end switch

    }



    // Normalize to remove the closing point by default.
    var lastPoint = points[ points.length - 1 ];
    if ( Math.abs( lastPoint.x - points[ 0 ].x ) < Number.EPSILON &&
        Math.abs( lastPoint.y - points[ 0 ].y ) < Number.EPSILON )
        points.splice( points.length - 1, 1 );
    if ( closedPath ) {

        points.push( points[ 0 ] );

    }

    return points;

};

//
// Breaks path into shapes
//
//	Assumptions (if parameter isCCW==true the opposite holds):
//	- solid shapes are defined clockwise (CW)
//	- holes are defined counterclockwise (CCW)
//
//	If parameter noHoles==true:
//  - all subPaths are regarded as solid shapes
//  - definition order CW/CCW has no relevance
//

THREE.Path.prototype.toShapes = function( isCCW, noHoles ) {

    function extractSubpaths( inActions ) {

        var subPaths = [], lastPath = new THREE.Path();

        for ( var i = 0, l = inActions.length; i < l; i ++ ) {

            var item = inActions[ i ];

            var args = item.args;
            var action = item.action;

            if ( action === 'moveTo' ) {

                if ( lastPath.actions.length !== 0 ) {

                    subPaths.push( lastPath );
                    lastPath = new THREE.Path();

                }

            }

            lastPath[ action ].apply( lastPath, args );

        }

        if ( lastPath.actions.length !== 0 ) {

            subPaths.push( lastPath );

        }

        // console.log(subPaths);

        return	subPaths;

    }

    function toShapesNoHoles( inSubpaths ) {

        var shapes = [];

        for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) {

            var tmpPath = inSubpaths[ i ];

            var tmpShape = new THREE.Shape();
            tmpShape.actions = tmpPath.actions;
            tmpShape.curves = tmpPath.curves;

            shapes.push( tmpShape );

        }

        //console.log("shape", shapes);

        return shapes;

    }

    function isPointInsidePolygon( inPt, inPolygon ) {

        var polyLen = inPolygon.length;

        // inPt on polygon contour => immediate success    or
        // toggling of inside/outside at every single! intersection point of an edge
        //  with the horizontal line through inPt, left of inPt
        //  not counting lowerY endpoints of edges and whole edges on that line
        var inside = false;
        for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {

            var edgeLowPt  = inPolygon[ p ];
            var edgeHighPt = inPolygon[ q ];

            var edgeDx = edgeHighPt.x - edgeLowPt.x;
            var edgeDy = edgeHighPt.y - edgeLowPt.y;

            if ( Math.abs( edgeDy ) > Number.EPSILON ) {

                // not parallel
                if ( edgeDy < 0 ) {

                    edgeLowPt  = inPolygon[ q ]; edgeDx = - edgeDx;
                    edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;

                }
                if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) 		continue;

                if ( inPt.y === edgeLowPt.y ) {

                    if ( inPt.x === edgeLowPt.x )		return	true;		// inPt is on contour ?
                    // continue;				// no intersection or edgeLowPt => doesn't count !!!

                } else {

                    var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );
                    if ( perpEdge === 0 )				return	true;		// inPt is on contour ?
                    if ( perpEdge < 0 ) 				continue;
                    inside = ! inside;		// true intersection left of inPt

                }

            } else {

                // parallel or collinear
                if ( inPt.y !== edgeLowPt.y ) 		continue;			// parallel
                // edge lies on the same horizontal line as inPt
                if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
                    ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) )		return	true;	// inPt: Point on contour !
                // continue;

            }

        }

        return	inside;

    }

    var isClockWise = THREE.ShapeUtils.isClockWise;

    var subPaths = extractSubpaths( this.actions );
    if ( subPaths.length === 0 ) return [];

    if ( noHoles === true )	return	toShapesNoHoles( subPaths );


    var solid, tmpPath, tmpShape, shapes = [];

    if ( subPaths.length === 1 ) {

        tmpPath = subPaths[ 0 ];
        tmpShape = new THREE.Shape();
        tmpShape.actions = tmpPath.actions;
        tmpShape.curves = tmpPath.curves;
        shapes.push( tmpShape );
        return shapes;

    }

    var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );
    holesFirst = isCCW ? ! holesFirst : holesFirst;

    // console.log("Holes first", holesFirst);

    var betterShapeHoles = [];
    var newShapes = [];
    var newShapeHoles = [];
    var mainIdx = 0;
    var tmpPoints;

    newShapes[ mainIdx ] = undefined;
    newShapeHoles[ mainIdx ] = [];

    for ( var i = 0, l = subPaths.length; i < l; i ++ ) {

        tmpPath = subPaths[ i ];
        tmpPoints = tmpPath.getPoints();
        solid = isClockWise( tmpPoints );
        solid = isCCW ? ! solid : solid;

        if ( solid ) {

            if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) )	mainIdx ++;

            newShapes[ mainIdx ] = { s: new THREE.Shape(), p: tmpPoints };
            newShapes[ mainIdx ].s.actions = tmpPath.actions;
            newShapes[ mainIdx ].s.curves = tmpPath.curves;

            if ( holesFirst )	mainIdx ++;
            newShapeHoles[ mainIdx ] = [];

            //console.log('cw', i);

        } else {

            newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );

            //console.log('ccw', i);

        }

    }

    // only Holes? -> probably all Shapes with wrong orientation
    if ( ! newShapes[ 0 ] )	return	toShapesNoHoles( subPaths );


    if ( newShapes.length > 1 ) {

        var ambiguous = false;
        var toChange = [];

        for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {

            betterShapeHoles[ sIdx ] = [];

        }

        for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {

            var sho = newShapeHoles[ sIdx ];

            for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) {

                var ho = sho[ hIdx ];
                var hole_unassigned = true;

                for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {

                    if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {

                        if ( sIdx !== s2Idx )	toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
                        if ( hole_unassigned ) {

                            hole_unassigned = false;
                            betterShapeHoles[ s2Idx ].push( ho );

                        } else {

                            ambiguous = true;

                        }

                    }

                }
                if ( hole_unassigned ) {

                    betterShapeHoles[ sIdx ].push( ho );

                }

            }

        }
        // console.log("ambiguous: ", ambiguous);
        if ( toChange.length > 0 ) {

            // console.log("to change: ", toChange);
            if ( ! ambiguous )	newShapeHoles = betterShapeHoles;

        }

    }

    var tmpHoles;

    for ( var i = 0, il = newShapes.length; i < il; i ++ ) {

        tmpShape = newShapes[ i ].s;
        shapes.push( tmpShape );
        tmpHoles = newShapeHoles[ i ];

        for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) {

            tmpShape.holes.push( tmpHoles[ j ].h );

        }

    }

    //console.log("shape", shapes);

    return shapes;

};

// File:src/extras/core/Shape.js

/**
 * @author zz85 / http://www.lab4games.net/zz85/blog
 * Defines a 2d shape plane using paths.
 **/

// STEP 1 Create a path.
// STEP 2 Turn path into shape.
// STEP 3 ExtrudeGeometry takes in Shape/Shapes
// STEP 3a - Extract points from each shape, turn to vertices
// STEP 3b - Triangulate each shape, add faces.

THREE.Shape = function () {

    THREE.Path.apply( this, arguments );

    this.holes = [];

};

THREE.Shape.prototype = Object.create( THREE.Path.prototype );
THREE.Shape.prototype.constructor = THREE.Shape;

// Convenience method to return ExtrudeGeometry

THREE.Shape.prototype.extrude = function ( options ) {

    return new THREE.ExtrudeGeometry( this, options );

};

// Convenience method to return ShapeGeometry

THREE.Shape.prototype.makeGeometry = function ( options ) {

    return new THREE.ShapeGeometry( this, options );

};

// Get points of holes

THREE.Shape.prototype.getPointsHoles = function ( divisions ) {

    var holesPts = [];

    for ( var i = 0, l = this.holes.length; i < l; i ++ ) {

        holesPts[ i ] = this.holes[ i ].getPoints( divisions );

    }

    return holesPts;

};


// Get points of shape and holes (keypoints based on segments parameter)

THREE.Shape.prototype.extractAllPoints = function ( divisions ) {

    return {

        shape: this.getPoints( divisions ),
        holes: this.getPointsHoles( divisions )

    };

};

THREE.Shape.prototype.extractPoints = function ( divisions ) {

    return this.extractAllPoints( divisions );

};

THREE.Shape.Utils = THREE.ShapeUtils; // backwards compatibility

// File:src/extras/curves/LineCurve.js

/**************************************************************
 *	Line
 **************************************************************/

THREE.LineCurve = function ( v1, v2 ) {

    this.v1 = v1;
    this.v2 = v2;

};

THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
THREE.LineCurve.prototype.constructor = THREE.LineCurve;

THREE.LineCurve.prototype.getPoint = function ( t ) {

    var point = this.v2.clone().sub( this.v1 );
    point.multiplyScalar( t ).add( this.v1 );

    return point;

};

// Line curve is linear, so we can overwrite default getPointAt

THREE.LineCurve.prototype.getPointAt = function ( u ) {

    return this.getPoint( u );

};

THREE.LineCurve.prototype.getTangent = function( t ) {

    var tangent = this.v2.clone().sub( this.v1 );

    return tangent.normalize();

};

// File:src/extras/curves/QuadraticBezierCurve.js

/**************************************************************
 *	Quadratic Bezier curve
 **************************************************************/


THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {

    this.v0 = v0;
    this.v1 = v1;
    this.v2 = v2;

};

THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve;


THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {

    var b2 = THREE.ShapeUtils.b2;

    return new THREE.Vector2(
        b2( t, this.v0.x, this.v1.x, this.v2.x ),
        b2( t, this.v0.y, this.v1.y, this.v2.y )
    );

};


THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {

    var tangentQuadraticBezier = THREE.CurveUtils.tangentQuadraticBezier;

    return new THREE.Vector2(
        tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ),
        tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y )
    ).normalize();

};

// File:src/extras/curves/CubicBezierCurve.js

/**************************************************************
 *	Cubic Bezier curve
 **************************************************************/

THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {

    this.v0 = v0;
    this.v1 = v1;
    this.v2 = v2;
    this.v3 = v3;

};

THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve;

THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {

    var b3 = THREE.ShapeUtils.b3;

    return new THREE.Vector2(
        b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ),
        b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y )
    );

};

THREE.CubicBezierCurve.prototype.getTangent = function( t ) {

    var tangentCubicBezier = THREE.CurveUtils.tangentCubicBezier;

    return new THREE.Vector2(
        tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ),
        tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y )
    ).normalize();

};

// File:src/extras/curves/SplineCurve.js

/**************************************************************
 *	Spline curve
 **************************************************************/

THREE.SplineCurve = function ( points /* array of Vector2 */ ) {

    this.points = ( points == undefined ) ? [] : points;

};

THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype );
THREE.SplineCurve.prototype.constructor = THREE.SplineCurve;

THREE.SplineCurve.prototype.getPoint = function ( t ) {

    var points = this.points;
    var point = ( points.length - 1 ) * t;

    var intPoint = Math.floor( point );
    var weight = point - intPoint;

    var point0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ];
    var point1 = points[ intPoint ];
    var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
    var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];

    var interpolate = THREE.CurveUtils.interpolate;

    return new THREE.Vector2(
        interpolate( point0.x, point1.x, point2.x, point3.x, weight ),
        interpolate( point0.y, point1.y, point2.y, point3.y, weight )
    );

};

// File:src/extras/curves/EllipseCurve.js

/**************************************************************
 *	Ellipse curve
 **************************************************************/

THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {

    this.aX = aX;
    this.aY = aY;

    this.xRadius = xRadius;
    this.yRadius = yRadius;

    this.aStartAngle = aStartAngle;
    this.aEndAngle = aEndAngle;

    this.aClockwise = aClockwise;

    this.aRotation = aRotation || 0;

};

THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
THREE.EllipseCurve.prototype.constructor = THREE.EllipseCurve;

THREE.EllipseCurve.prototype.getPoint = function ( t ) {

    var deltaAngle = this.aEndAngle - this.aStartAngle;

    if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2;
    if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2;

    var angle;

    if ( this.aClockwise === true ) {

        angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle );

    } else {

        angle = this.aStartAngle + t * deltaAngle;

    }

    var x = this.aX + this.xRadius * Math.cos( angle );
    var y = this.aY + this.yRadius * Math.sin( angle );

    if ( this.aRotation !== 0 ) {

        var cos = Math.cos( this.aRotation );
        var sin = Math.sin( this.aRotation );

        var tx = x, ty = y;

        // Rotate the point about the center of the ellipse.
        x = ( tx - this.aX ) * cos - ( ty - this.aY ) * sin + this.aX;
        y = ( tx - this.aX ) * sin + ( ty - this.aY ) * cos + this.aY;

    }

    return new THREE.Vector2( x, y );

};

// File:src/extras/curves/ArcCurve.js

/**************************************************************
 *	Arc curve
 **************************************************************/

THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {

    THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );

};

THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
THREE.ArcCurve.prototype.constructor = THREE.ArcCurve;

// File:src/extras/curves/LineCurve3.js

/**************************************************************
 *	Line3D
 **************************************************************/

THREE.LineCurve3 = THREE.Curve.create(

    function ( v1, v2 ) {

        this.v1 = v1;
        this.v2 = v2;

    },

    function ( t ) {

        var vector = new THREE.Vector3();

        vector.subVectors( this.v2, this.v1 ); // diff
        vector.multiplyScalar( t );
        vector.add( this.v1 );

        return vector;

    }

);

// File:src/extras/curves/QuadraticBezierCurve3.js

/**************************************************************
 *	Quadratic Bezier 3D curve
 **************************************************************/

THREE.QuadraticBezierCurve3 = THREE.Curve.create(

    function ( v0, v1, v2 ) {

        this.v0 = v0;
        this.v1 = v1;
        this.v2 = v2;

    },

    function ( t ) {

        var b2 = THREE.ShapeUtils.b2;

        return new THREE.Vector3(
            b2( t, this.v0.x, this.v1.x, this.v2.x ),
            b2( t, this.v0.y, this.v1.y, this.v2.y ),
            b2( t, this.v0.z, this.v1.z, this.v2.z )
        );

    }

);

// File:src/extras/curves/CubicBezierCurve3.js

/**************************************************************
 *	Cubic Bezier 3D curve
 **************************************************************/

THREE.CubicBezierCurve3 = THREE.Curve.create(

    function ( v0, v1, v2, v3 ) {

        this.v0 = v0;
        this.v1 = v1;
        this.v2 = v2;
        this.v3 = v3;

    },

    function ( t ) {

        var b3 = THREE.ShapeUtils.b3;

        return new THREE.Vector3(
            b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ),
            b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ),
            b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z )
        );

    }

);

// File:src/extras/curves/SplineCurve3.js

/**************************************************************
 *	Spline 3D curve
 **************************************************************/


THREE.SplineCurve3 = THREE.Curve.create(

    function ( points /* array of Vector3 */ ) {

        console.warn( 'THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3' );
        this.points = ( points == undefined ) ? [] : points;

    },

    function ( t ) {

        var points = this.points;
        var point = ( points.length - 1 ) * t;

        var intPoint = Math.floor( point );
        var weight = point - intPoint;

        var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ];
        var point1 = points[ intPoint ];
        var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
        var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];

        var interpolate = THREE.CurveUtils.interpolate;

        return new THREE.Vector3(
            interpolate( point0.x, point1.x, point2.x, point3.x, weight ),
            interpolate( point0.y, point1.y, point2.y, point3.y, weight ),
            interpolate( point0.z, point1.z, point2.z, point3.z, weight )
        );

    }

);

// File:src/extras/curves/CatmullRomCurve3.js

/**
 * @author zz85 https://github.com/zz85
 *
 * Centripetal CatmullRom Curve - which is useful for avoiding
 * cusps and self-intersections in non-uniform catmull rom curves.
 * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
 *
 * curve.type accepts centripetal(default), chordal and catmullrom
 * curve.tension is used for catmullrom which defaults to 0.5
 */

THREE.CatmullRomCurve3 = ( function() {

    var
        tmp = new THREE.Vector3(),
        px = new CubicPoly(),
        py = new CubicPoly(),
        pz = new CubicPoly();

    /*
	Based on an optimized c++ solution in
	 - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/
	 - http://ideone.com/NoEbVM

	This CubicPoly class could be used for reusing some variables and calculations,
	but for three.js curve use, it could be possible inlined and flatten into a single function call
	which can be placed in CurveUtils.
	*/

    function CubicPoly() {

    }

    /*
	 * Compute coefficients for a cubic polynomial
	 *   p(s) = c0 + c1*s + c2*s^2 + c3*s^3
	 * such that
	 *   p(0) = x0, p(1) = x1
	 *  and
	 *   p'(0) = t0, p'(1) = t1.
	 */
    CubicPoly.prototype.init = function( x0, x1, t0, t1 ) {

        this.c0 = x0;
        this.c1 = t0;
        this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1;
        this.c3 = 2 * x0 - 2 * x1 + t0 + t1;

    };

    CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) {

        // compute tangents when parameterized in [t1,t2]
        var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;
        var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;

        // rescale tangents for parametrization in [0,1]
        t1 *= dt1;
        t2 *= dt1;

        // initCubicPoly
        this.init( x1, x2, t1, t2 );

    };

    // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4
    CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) {

        this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );

    };

    CubicPoly.prototype.calc = function( t ) {

        var t2 = t * t;
        var t3 = t2 * t;
        return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3;

    };

    // Subclass Three.js curve
    return THREE.Curve.create(

        function ( p /* array of Vector3 */ ) {

            this.points = p || [];

        },

        function ( t ) {

            var points = this.points,
                point, intPoint, weight, l;

            l = points.length;

            if ( l < 2 ) console.log( 'duh, you need at least 2 points' );

            point = ( l - 1 ) * t;
            intPoint = Math.floor( point );
            weight = point - intPoint;

            if ( weight === 0 && intPoint === l - 1 ) {

                intPoint = l - 2;
                weight = 1;

            }

            var p0, p1, p2, p3;

            if ( intPoint === 0 ) {

                // extrapolate first point
                tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] );
                p0 = tmp;

            } else {

                p0 = points[ intPoint - 1 ];

            }

            p1 = points[ intPoint ];
            p2 = points[ intPoint + 1 ];

            if ( intPoint + 2 < l ) {

                p3 = points[ intPoint + 2 ]

            } else {

                // extrapolate last point
                tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 2 ] );
                p3 = tmp;

            }

            if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) {

                // init Centripetal / Chordal Catmull-Rom
                var pow = this.type === 'chordal' ? 0.5 : 0.25;
                var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );
                var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );
                var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );

                // safety check for repeated points
                if ( dt1 < 1e-4 ) dt1 = 1.0;
                if ( dt0 < 1e-4 ) dt0 = dt1;
                if ( dt2 < 1e-4 ) dt2 = dt1;

                px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 );
                py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );
                pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );

            } else if ( this.type === 'catmullrom' ) {

                var tension = this.tension !== undefined ? this.tension : 0.5;
                px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension );
                py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension );
                pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension );

            }

            var v = new THREE.Vector3(
                px.calc( weight ),
                py.calc( weight ),
                pz.calc( weight )
            );

            return v;

        }

    );

} )();

// File:src/extras/curves/ClosedSplineCurve3.js

/**************************************************************
 *	Closed Spline 3D curve
 **************************************************************/


THREE.ClosedSplineCurve3 = THREE.Curve.create(

    function ( points /* array of Vector3 */ ) {

        this.points = ( points == undefined ) ? [] : points;

    },

    function ( t ) {

        var points = this.points;
        var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1

        var intPoint = Math.floor( point );
        var weight = point - intPoint;

        intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;

        var point0 = points[ ( intPoint - 1 ) % points.length ];
        var point1 = points[ ( intPoint     ) % points.length ];
        var point2 = points[ ( intPoint + 1 ) % points.length ];
        var point3 = points[ ( intPoint + 2 ) % points.length ];

        var interpolate = THREE.CurveUtils.interpolate;

        return new THREE.Vector3(
            interpolate( point0.x, point1.x, point2.x, point3.x, weight ),
            interpolate( point0.y, point1.y, point2.y, point3.y, weight ),
            interpolate( point0.z, point1.z, point2.z, point3.z, weight )
        );

    }

);

// File:src/extras/geometries/BoxGeometry.js

/**
 * @author mrdoob / http://mrdoob.com/
 * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
 */

THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {

    THREE.Geometry.call( this );

    this.type = 'BoxGeometry';

    this.parameters = {
        width: width,
        height: height,
        depth: depth,
        widthSegments: widthSegments,
        heightSegments: heightSegments,
        depthSegments: depthSegments
    };

    this.widthSegments = widthSegments || 1;
    this.heightSegments = heightSegments || 1;
    this.depthSegments = depthSegments || 1;

    var scope = this;

    var width_half = width / 2;
    var height_half = height / 2;
    var depth_half = depth / 2;

    buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px
    buildPlane( 'z', 'y',   1, - 1, depth, height, - width_half, 1 ); // nx
    buildPlane( 'x', 'z',   1,   1, width, depth, height_half, 2 ); // py
    buildPlane( 'x', 'z',   1, - 1, width, depth, - height_half, 3 ); // ny
    buildPlane( 'x', 'y',   1, - 1, width, height, depth_half, 4 ); // pz
    buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz

    function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) {

        var w, ix, iy,
            gridX = scope.widthSegments,
            gridY = scope.heightSegments,
            width_half = width / 2,
            height_half = height / 2,
            offset = scope.vertices.length;

        if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {

            w = 'z';

        } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {

            w = 'y';
            gridY = scope.depthSegments;

        } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {

            w = 'x';
            gridX = scope.depthSegments;

        }

        var gridX1 = gridX + 1,
            gridY1 = gridY + 1,
            segment_width = width / gridX,
            segment_height = height / gridY,
            normal = new THREE.Vector3();

        normal[ w ] = depth > 0 ? 1 : - 1;

        for ( iy = 0; iy < gridY1; iy ++ ) {

            for ( ix = 0; ix < gridX1; ix ++ ) {

                var vector = new THREE.Vector3();
                vector[ u ] = ( ix * segment_width - width_half ) * udir;
                vector[ v ] = ( iy * segment_height - height_half ) * vdir;
                vector[ w ] = depth;

                scope.vertices.push( vector );

            }

        }

        for ( iy = 0; iy < gridY; iy ++ ) {

            for ( ix = 0; ix < gridX; ix ++ ) {

                var a = ix + gridX1 * iy;
                var b = ix + gridX1 * ( iy + 1 );
                var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
                var d = ( ix + 1 ) + gridX1 * iy;

                var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY );
                var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY );
                var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY );
                var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY );

                var face = new THREE.Face3( a + offset, b + offset, d + offset );
                face.normal.copy( normal );
                face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
                face.materialIndex = materialIndex;

                scope.faces.push( face );
                scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );

                face = new THREE.Face3( b + offset, c + offset, d + offset );
                face.normal.copy( normal );
                face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
                face.materialIndex = materialIndex;

                scope.faces.push( face );
                scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );

            }

        }

    }

    this.mergeVertices();

};

THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.BoxGeometry.prototype.constructor = THREE.BoxGeometry;

THREE.BoxGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.BoxGeometry(
        parameters.width,
        parameters.height,
        parameters.depth,
        parameters.widthSegments,
        parameters.heightSegments,
        parameters.depthSegments
    );

};

THREE.CubeGeometry = THREE.BoxGeometry; // backwards compatibility

// File:src/extras/geometries/CircleGeometry.js

/**
 * @author hughes
 */

THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {

    THREE.Geometry.call( this );

    this.type = 'CircleGeometry';

    this.parameters = {
        radius: radius,
        segments: segments,
        thetaStart: thetaStart,
        thetaLength: thetaLength
    };

    this.fromBufferGeometry( new THREE.CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) );

};

THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.CircleGeometry.prototype.constructor = THREE.CircleGeometry;

THREE.CircleGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.CircleGeometry(
        parameters.radius,
        parameters.segments,
        parameters.thetaStart,
        parameters.thetaLength
    );

};

// File:src/extras/geometries/CircleBufferGeometry.js

/**
 * @author benaadams / https://twitter.com/ben_a_adams
 */

THREE.CircleBufferGeometry = function ( radius, segments, thetaStart, thetaLength ) {

    THREE.BufferGeometry.call( this );

    this.type = 'CircleBufferGeometry';

    this.parameters = {
        radius: radius,
        segments: segments,
        thetaStart: thetaStart,
        thetaLength: thetaLength
    };

    radius = radius || 50;
    segments = segments !== undefined ? Math.max( 3, segments ) : 8;

    thetaStart = thetaStart !== undefined ? thetaStart : 0;
    thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;

    var vertices = segments + 2;

    var positions = new Float32Array( vertices * 3 );
    var normals = new Float32Array( vertices * 3 );
    var uvs = new Float32Array( vertices * 2 );

    // center data is already zero, but need to set a few extras
    normals[ 2 ] = 1.0;
    uvs[ 0 ] = 0.5;
    uvs[ 1 ] = 0.5;

    for ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) {

        var segment = thetaStart + s / segments * thetaLength;

        positions[ i ] = radius * Math.cos( segment );
        positions[ i + 1 ] = radius * Math.sin( segment );

        normals[ i + 2 ] = 1; // normal z

        uvs[ ii ] = ( positions[ i ] / radius + 1 ) / 2;
        uvs[ ii + 1 ] = ( positions[ i + 1 ] / radius + 1 ) / 2;

    }

    var indices = [];

    for ( var i = 1; i <= segments; i ++ ) {

        indices.push( i, i + 1, 0 );

    }

    this.setIndex( new THREE.BufferAttribute( new Uint16Array( indices ), 1 ) );
    this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
    this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
    this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );

    this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );

};

THREE.CircleBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
THREE.CircleBufferGeometry.prototype.constructor = THREE.CircleBufferGeometry;

THREE.CircleBufferGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.CircleBufferGeometry(
        parameters.radius,
        parameters.segments,
        parameters.thetaStart,
        parameters.thetaLength
    );

};

// File:src/extras/geometries/CylinderGeometry.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {

    THREE.Geometry.call( this );

    this.type = 'CylinderGeometry';

    this.parameters = {
        radiusTop: radiusTop,
        radiusBottom: radiusBottom,
        height: height,
        radialSegments: radialSegments,
        heightSegments: heightSegments,
        openEnded: openEnded,
        thetaStart: thetaStart,
        thetaLength: thetaLength
    };

    radiusTop = radiusTop !== undefined ? radiusTop : 20;
    radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
    height = height !== undefined ? height : 100;

    radialSegments = radialSegments || 8;
    heightSegments = heightSegments || 1;

    openEnded = openEnded !== undefined ? openEnded : false;
    thetaStart = thetaStart !== undefined ? thetaStart : 0;
    thetaLength = thetaLength !== undefined ? thetaLength : 2 * Math.PI;

    var heightHalf = height / 2;

    var x, y, vertices = [], uvs = [];

    for ( y = 0; y <= heightSegments; y ++ ) {

        var verticesRow = [];
        var uvsRow = [];

        var v = y / heightSegments;
        var radius = v * ( radiusBottom - radiusTop ) + radiusTop;

        for ( x = 0; x <= radialSegments; x ++ ) {

            var u = x / radialSegments;

            var vertex = new THREE.Vector3();
            vertex.x = radius * Math.sin( u * thetaLength + thetaStart );
            vertex.y = - v * height + heightHalf;
            vertex.z = radius * Math.cos( u * thetaLength + thetaStart );

            this.vertices.push( vertex );

            verticesRow.push( this.vertices.length - 1 );
            uvsRow.push( new THREE.Vector2( u, 1 - v ) );

        }

        vertices.push( verticesRow );
        uvs.push( uvsRow );

    }

    var tanTheta = ( radiusBottom - radiusTop ) / height;
    var na, nb;

    for ( x = 0; x < radialSegments; x ++ ) {

        if ( radiusTop !== 0 ) {

            na = this.vertices[ vertices[ 0 ][ x ] ].clone();
            nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone();

        } else {

            na = this.vertices[ vertices[ 1 ][ x ] ].clone();
            nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone();

        }

        na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize();
        nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize();

        for ( y = 0; y < heightSegments; y ++ ) {

            var v1 = vertices[ y ][ x ];
            var v2 = vertices[ y + 1 ][ x ];
            var v3 = vertices[ y + 1 ][ x + 1 ];
            var v4 = vertices[ y ][ x + 1 ];

            var n1 = na.clone();
            var n2 = na.clone();
            var n3 = nb.clone();
            var n4 = nb.clone();

            var uv1 = uvs[ y ][ x ].clone();
            var uv2 = uvs[ y + 1 ][ x ].clone();
            var uv3 = uvs[ y + 1 ][ x + 1 ].clone();
            var uv4 = uvs[ y ][ x + 1 ].clone();

            this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) );
            this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] );

            this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) );
            this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );

        }

    }

    // top cap

    if ( openEnded === false && radiusTop > 0 ) {

        this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) );

        for ( x = 0; x < radialSegments; x ++ ) {

            var v1 = vertices[ 0 ][ x ];
            var v2 = vertices[ 0 ][ x + 1 ];
            var v3 = this.vertices.length - 1;

            var n1 = new THREE.Vector3( 0, 1, 0 );
            var n2 = new THREE.Vector3( 0, 1, 0 );
            var n3 = new THREE.Vector3( 0, 1, 0 );

            var uv1 = uvs[ 0 ][ x ].clone();
            var uv2 = uvs[ 0 ][ x + 1 ].clone();
            var uv3 = new THREE.Vector2( uv2.x, 0 );

            this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 1 ) );
            this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );

        }

    }

    // bottom cap

    if ( openEnded === false && radiusBottom > 0 ) {

        this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) );

        for ( x = 0; x < radialSegments; x ++ ) {

            var v1 = vertices[ heightSegments ][ x + 1 ];
            var v2 = vertices[ heightSegments ][ x ];
            var v3 = this.vertices.length - 1;

            var n1 = new THREE.Vector3( 0, - 1, 0 );
            var n2 = new THREE.Vector3( 0, - 1, 0 );
            var n3 = new THREE.Vector3( 0, - 1, 0 );

            var uv1 = uvs[ heightSegments ][ x + 1 ].clone();
            var uv2 = uvs[ heightSegments ][ x ].clone();
            var uv3 = new THREE.Vector2( uv2.x, 1 );

            this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ], undefined, 2 ) );
            this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );

        }

    }

    this.computeFaceNormals();

};

THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry;

THREE.CylinderGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.CylinderGeometry(
        parameters.radiusTop,
        parameters.radiusBottom,
        parameters.height,
        parameters.radialSegments,
        parameters.heightSegments,
        parameters.openEnded,
        parameters.thetaStart,
        parameters.thetaLength
    );

};

// File:src/extras/geometries/EdgesGeometry.js

/**
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.EdgesGeometry = function ( geometry, thresholdAngle ) {

    THREE.BufferGeometry.call( this );

    thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1;

    var thresholdDot = Math.cos( THREE.Math.degToRad( thresholdAngle ) );

    var edge = [ 0, 0 ], hash = {};

    function sortFunction( a, b ) {

        return a - b;

    }

    var keys = [ 'a', 'b', 'c' ];

    var geometry2;

    if ( geometry instanceof THREE.BufferGeometry ) {

        geometry2 = new THREE.Geometry();
        geometry2.fromBufferGeometry( geometry );

    } else {

        geometry2 = geometry.clone();

    }

    geometry2.mergeVertices();
    geometry2.computeFaceNormals();

    var vertices = geometry2.vertices;
    var faces = geometry2.faces;

    for ( var i = 0, l = faces.length; i < l; i ++ ) {

        var face = faces[ i ];

        for ( var j = 0; j < 3; j ++ ) {

            edge[ 0 ] = face[ keys[ j ] ];
            edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
            edge.sort( sortFunction );

            var key = edge.toString();

            if ( hash[ key ] === undefined ) {

                hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined };

            } else {

                hash[ key ].face2 = i;

            }

        }

    }

    var coords = [];

    for ( var key in hash ) {

        var h = hash[ key ];

        if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) {

            var vertex = vertices[ h.vert1 ];
            coords.push( vertex.x );
            coords.push( vertex.y );
            coords.push( vertex.z );

            vertex = vertices[ h.vert2 ];
            coords.push( vertex.x );
            coords.push( vertex.y );
            coords.push( vertex.z );

        }

    }

    this.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( coords ), 3 ) );

};

THREE.EdgesGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
THREE.EdgesGeometry.prototype.constructor = THREE.EdgesGeometry;

// File:src/extras/geometries/ExtrudeGeometry.js

/**
 * @author zz85 / http://www.lab4games.net/zz85/blog
 *
 * Creates extruded geometry from a path shape.
 *
 * parameters = {
 *
 *  curveSegments: <int>, // number of points on the curves
 *  steps: <int>, // number of points for z-side extrusions / used for subdividing segments of extrude spline too
 *  amount: <int>, // Depth to extrude the shape
 *
 *  bevelEnabled: <bool>, // turn on bevel
 *  bevelThickness: <float>, // how deep into the original shape bevel goes
 *  bevelSize: <float>, // how far from shape outline is bevel
 *  bevelSegments: <int>, // number of bevel layers
 *
 *  extrudePath: <THREE.CurvePath> // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined)
 *  frames: <THREE.TubeGeometry.FrenetFrames> // containing arrays of tangents, normals, binormals
 *
 *  uvGenerator: <Object> // object that provides UV generator functions
 *
 * }
 **/

THREE.ExtrudeGeometry = function ( shapes, options ) {

    if ( typeof( shapes ) === "undefined" ) {

        shapes = [];
        return;

    }

    THREE.Geometry.call( this );

    this.type = 'ExtrudeGeometry';

    shapes = Array.isArray( shapes ) ? shapes : [ shapes ];

    this.addShapeList( shapes, options );

    this.computeFaceNormals();

    // can't really use automatic vertex normals
    // as then front and back sides get smoothed too
    // should do separate smoothing just for sides

    //this.computeVertexNormals();

    //console.log( "took", ( Date.now() - startTime ) );

};

THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry;

THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) {

    var sl = shapes.length;

    for ( var s = 0; s < sl; s ++ ) {

        var shape = shapes[ s ];
        this.addShape( shape, options );

    }

};

THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) {

    var amount = options.amount !== undefined ? options.amount : 100;

    var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
    var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
    var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;

    var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false

    var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;

    var steps = options.steps !== undefined ? options.steps : 1;

    var extrudePath = options.extrudePath;
    var extrudePts, extrudeByPath = false;

    // Use default WorldUVGenerator if no UV generators are specified.
    var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator;

    var splineTube, binormal, normal, position2;
    if ( extrudePath ) {

        extrudePts = extrudePath.getSpacedPoints( steps );

        extrudeByPath = true;
        bevelEnabled = false; // bevels not supported for path extrusion

        // SETUP TNB variables

        // Reuse TNB from TubeGeomtry for now.
        // TODO1 - have a .isClosed in spline?

        splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames( extrudePath, steps, false );

        // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);

        binormal = new THREE.Vector3();
        normal = new THREE.Vector3();
        position2 = new THREE.Vector3();

    }

    // Safeguards if bevels are not enabled

    if ( ! bevelEnabled ) {

        bevelSegments = 0;
        bevelThickness = 0;
        bevelSize = 0;

    }

    // Variables initialization

    var ahole, h, hl; // looping of holes
    var scope = this;

    var shapesOffset = this.vertices.length;

    var shapePoints = shape.extractPoints( curveSegments );

    var vertices = shapePoints.shape;
    var holes = shapePoints.holes;

    var reverse = ! THREE.ShapeUtils.isClockWise( vertices );

    if ( reverse ) {

        vertices = vertices.reverse();

        // Maybe we should also check if holes are in the opposite direction, just to be safe ...

        for ( h = 0, hl = holes.length; h < hl; h ++ ) {

            ahole = holes[ h ];

            if ( THREE.ShapeUtils.isClockWise( ahole ) ) {

                holes[ h ] = ahole.reverse();

            }

        }

        reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!

    }


    var faces = THREE.ShapeUtils.triangulateShape( vertices, holes );

    /* Vertices */

    var contour = vertices; // vertices has all points but contour has only points of circumference

    for ( h = 0, hl = holes.length; h < hl; h ++ ) {

        ahole = holes[ h ];

        vertices = vertices.concat( ahole );

    }


    function scalePt2 ( pt, vec, size ) {

        if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" );

        return vec.clone().multiplyScalar( size ).add( pt );

    }

    var b, bs, t, z,
        vert, vlen = vertices.length,
        face, flen = faces.length;


    // Find directions for point movement


    function getBevelVec( inPt, inPrev, inNext ) {

        // computes for inPt the corresponding point inPt' on a new contour
        //   shifted by 1 unit (length of normalized vector) to the left
        // if we walk along contour clockwise, this new contour is outside the old one
        //
        // inPt' is the intersection of the two lines parallel to the two
        //  adjacent edges of inPt at a distance of 1 unit on the left side.

        var v_trans_x, v_trans_y, shrink_by = 1;		// resulting translation vector for inPt

        // good reading for geometry algorithms (here: line-line intersection)
        // http://geomalgorithms.com/a05-_intersect-1.html

        var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y;
        var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y;

        var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );

        // check for collinear edges
        var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );

        if ( Math.abs( collinear0 ) > Number.EPSILON ) {

            // not collinear

            // length of vectors for normalizing

            var v_prev_len = Math.sqrt( v_prev_lensq );
            var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );

            // shift adjacent points by unit vectors to the left

            var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );
            var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );

            var ptNextShift_x = ( inNext.x - v_next_y / v_next_len );
            var ptNextShift_y = ( inNext.y + v_next_x / v_next_len );

            // scaling factor for v_prev to intersection point

            var sf = (  ( ptNextShift_x - ptPrevShift_x ) * v_next_y -
                ( ptNextShift_y - ptPrevShift_y ) * v_next_x    ) /
                ( v_prev_x * v_next_y - v_prev_y * v_next_x );

            // vector from inPt to intersection point

            v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );
            v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );

            // Don't normalize!, otherwise sharp corners become ugly
            //  but prevent crazy spikes
            var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y );
            if ( v_trans_lensq <= 2 ) {

                return	new THREE.Vector2( v_trans_x, v_trans_y );

            } else {

                shrink_by = Math.sqrt( v_trans_lensq / 2 );

            }

        } else {

            // handle special case of collinear edges

            var direction_eq = false;		// assumes: opposite
            if ( v_prev_x > Number.EPSILON ) {

                if ( v_next_x > Number.EPSILON ) {

                    direction_eq = true;

                }

            } else {

                if ( v_prev_x < - Number.EPSILON ) {

                    if ( v_next_x < - Number.EPSILON ) {

                        direction_eq = true;

                    }

                } else {

                    if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) {

                        direction_eq = true;

                    }

                }

            }

            if ( direction_eq ) {

                // console.log("Warning: lines are a straight sequence");
                v_trans_x = - v_prev_y;
                v_trans_y =  v_prev_x;
                shrink_by = Math.sqrt( v_prev_lensq );

            } else {

                // console.log("Warning: lines are a straight spike");
                v_trans_x = v_prev_x;
                v_trans_y = v_prev_y;
                shrink_by = Math.sqrt( v_prev_lensq / 2 );

            }

        }

        return	new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );

    }


    var contourMovements = [];

    for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {

        if ( j === il ) j = 0;
        if ( k === il ) k = 0;

        //  (j)---(i)---(k)
        // console.log('i,j,k', i, j , k)

        contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] );

    }

    var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();

    for ( h = 0, hl = holes.length; h < hl; h ++ ) {

        ahole = holes[ h ];

        oneHoleMovements = [];

        for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {

            if ( j === il ) j = 0;
            if ( k === il ) k = 0;

            //  (j)---(i)---(k)
            oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );

        }

        holesMovements.push( oneHoleMovements );
        verticesMovements = verticesMovements.concat( oneHoleMovements );

    }


    // Loop bevelSegments, 1 for the front, 1 for the back

    for ( b = 0; b < bevelSegments; b ++ ) {

        //for ( b = bevelSegments; b > 0; b -- ) {

        t = b / bevelSegments;
        z = bevelThickness * ( 1 - t );

        //z = bevelThickness * t;
        bs = bevelSize * ( Math.sin ( t * Math.PI / 2 ) ); // curved
        //bs = bevelSize * t; // linear

        // contract shape

        for ( i = 0, il = contour.length; i < il; i ++ ) {

            vert = scalePt2( contour[ i ], contourMovements[ i ], bs );

            v( vert.x, vert.y,  - z );

        }

        // expand holes

        for ( h = 0, hl = holes.length; h < hl; h ++ ) {

            ahole = holes[ h ];
            oneHoleMovements = holesMovements[ h ];

            for ( i = 0, il = ahole.length; i < il; i ++ ) {

                vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );

                v( vert.x, vert.y,  - z );

            }

        }

    }

    bs = bevelSize;

    // Back facing vertices

    for ( i = 0; i < vlen; i ++ ) {

        vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];

        if ( ! extrudeByPath ) {

            v( vert.x, vert.y, 0 );

        } else {

            // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );

            normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x );
            binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y );

            position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal );

            v( position2.x, position2.y, position2.z );

        }

    }

    // Add stepped vertices...
    // Including front facing vertices

    var s;

    for ( s = 1; s <= steps; s ++ ) {

        for ( i = 0; i < vlen; i ++ ) {

            vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];

            if ( ! extrudeByPath ) {

                v( vert.x, vert.y, amount / steps * s );

            } else {

                // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );

                normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x );
                binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y );

                position2.copy( extrudePts[ s ] ).add( normal ).add( binormal );

                v( position2.x, position2.y, position2.z );

            }

        }

    }


    // Add bevel segments planes

    //for ( b = 1; b <= bevelSegments; b ++ ) {
    for ( b = bevelSegments - 1; b >= 0; b -- ) {

        t = b / bevelSegments;
        z = bevelThickness * ( 1 - t );
        //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
        bs = bevelSize * Math.sin ( t * Math.PI / 2 );

        // contract shape

        for ( i = 0, il = contour.length; i < il; i ++ ) {

            vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
            v( vert.x, vert.y,  amount + z );

        }

        // expand holes

        for ( h = 0, hl = holes.length; h < hl; h ++ ) {

            ahole = holes[ h ];
            oneHoleMovements = holesMovements[ h ];

            for ( i = 0, il = ahole.length; i < il; i ++ ) {

                vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );

                if ( ! extrudeByPath ) {

                    v( vert.x, vert.y,  amount + z );

                } else {

                    v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );

                }

            }

        }

    }

    /* Faces */

    // Top and bottom faces

    buildLidFaces();

    // Sides faces

    buildSideFaces();


    /////  Internal functions

    function buildLidFaces() {

        if ( bevelEnabled ) {

            var layer = 0; // steps + 1
            var offset = vlen * layer;

            // Bottom faces

            for ( i = 0; i < flen; i ++ ) {

                face = faces[ i ];
                f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset );

            }

            layer = steps + bevelSegments * 2;
            offset = vlen * layer;

            // Top faces

            for ( i = 0; i < flen; i ++ ) {

                face = faces[ i ];
                f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );

            }

        } else {

            // Bottom faces

            for ( i = 0; i < flen; i ++ ) {

                face = faces[ i ];
                f3( face[ 2 ], face[ 1 ], face[ 0 ] );

            }

            // Top faces

            for ( i = 0; i < flen; i ++ ) {

                face = faces[ i ];
                f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );

            }

        }

    }

    // Create faces for the z-sides of the shape

    function buildSideFaces() {

        var layeroffset = 0;
        sidewalls( contour, layeroffset );
        layeroffset += contour.length;

        for ( h = 0, hl = holes.length; h < hl; h ++ ) {

            ahole = holes[ h ];
            sidewalls( ahole, layeroffset );

            //, true
            layeroffset += ahole.length;

        }

    }

    function sidewalls( contour, layeroffset ) {

        var j, k;
        i = contour.length;

        while ( -- i >= 0 ) {

            j = i;
            k = i - 1;
            if ( k < 0 ) k = contour.length - 1;

            //console.log('b', i,j, i-1, k,vertices.length);

            var s = 0, sl = steps  + bevelSegments * 2;

            for ( s = 0; s < sl; s ++ ) {

                var slen1 = vlen * s;
                var slen2 = vlen * ( s + 1 );

                var a = layeroffset + j + slen1,
                    b = layeroffset + k + slen1,
                    c = layeroffset + k + slen2,
                    d = layeroffset + j + slen2;

                f4( a, b, c, d, contour, s, sl, j, k );

            }

        }

    }


    function v( x, y, z ) {

        scope.vertices.push( new THREE.Vector3( x, y, z ) );

    }

    function f3( a, b, c ) {

        a += shapesOffset;
        b += shapesOffset;
        c += shapesOffset;

        scope.faces.push( new THREE.Face3( a, b, c, null, null, 0 ) );

        var uvs = uvgen.generateTopUV( scope, a, b, c );

        scope.faceVertexUvs[ 0 ].push( uvs );

    }

    function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {

        a += shapesOffset;
        b += shapesOffset;
        c += shapesOffset;
        d += shapesOffset;

        scope.faces.push( new THREE.Face3( a, b, d, null, null, 1 ) );
        scope.faces.push( new THREE.Face3( b, c, d, null, null, 1 ) );

        var uvs = uvgen.generateSideWallUV( scope, a, b, c, d );

        scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] );
        scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] );

    }

};

THREE.ExtrudeGeometry.WorldUVGenerator = {

    generateTopUV: function ( geometry, indexA, indexB, indexC ) {

        var vertices = geometry.vertices;

        var a = vertices[ indexA ];
        var b = vertices[ indexB ];
        var c = vertices[ indexC ];

        return [
            new THREE.Vector2( a.x, a.y ),
            new THREE.Vector2( b.x, b.y ),
            new THREE.Vector2( c.x, c.y )
        ];

    },

    generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) {

        var vertices = geometry.vertices;

        var a = vertices[ indexA ];
        var b = vertices[ indexB ];
        var c = vertices[ indexC ];
        var d = vertices[ indexD ];

        if ( Math.abs( a.y - b.y ) < 0.01 ) {

            return [
                new THREE.Vector2( a.x, 1 - a.z ),
                new THREE.Vector2( b.x, 1 - b.z ),
                new THREE.Vector2( c.x, 1 - c.z ),
                new THREE.Vector2( d.x, 1 - d.z )
            ];

        } else {

            return [
                new THREE.Vector2( a.y, 1 - a.z ),
                new THREE.Vector2( b.y, 1 - b.z ),
                new THREE.Vector2( c.y, 1 - c.z ),
                new THREE.Vector2( d.y, 1 - d.z )
            ];

        }

    }
};

// File:src/extras/geometries/ShapeGeometry.js

/**
 * @author jonobr1 / http://jonobr1.com
 *
 * Creates a one-sided polygonal geometry from a path shape. Similar to
 * ExtrudeGeometry.
 *
 * parameters = {
 *
 *	curveSegments: <int>, // number of points on the curves. NOT USED AT THE MOMENT.
 *
 *	material: <int> // material index for front and back faces
 *	uvGenerator: <Object> // object that provides UV generator functions
 *
 * }
 **/

THREE.ShapeGeometry = function ( shapes, options ) {

    THREE.Geometry.call( this );

    this.type = 'ShapeGeometry';

    if ( Array.isArray( shapes ) === false ) shapes = [ shapes ];

    this.addShapeList( shapes, options );

    this.computeFaceNormals();

};

THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.ShapeGeometry.prototype.constructor = THREE.ShapeGeometry;

/**
 * Add an array of shapes to THREE.ShapeGeometry.
 */
THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) {

    for ( var i = 0, l = shapes.length; i < l; i ++ ) {

        this.addShape( shapes[ i ], options );

    }

    return this;

};

/**
 * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry.
 */
THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) {

    if ( options === undefined ) options = {};
    var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;

    var material = options.material;
    var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator;

    //

    var i, l, hole;

    var shapesOffset = this.vertices.length;
    var shapePoints = shape.extractPoints( curveSegments );

    var vertices = shapePoints.shape;
    var holes = shapePoints.holes;

    var reverse = ! THREE.ShapeUtils.isClockWise( vertices );

    if ( reverse ) {

        vertices = vertices.reverse();

        // Maybe we should also check if holes are in the opposite direction, just to be safe...

        for ( i = 0, l = holes.length; i < l; i ++ ) {

            hole = holes[ i ];

            if ( THREE.ShapeUtils.isClockWise( hole ) ) {

                holes[ i ] = hole.reverse();

            }

        }

        reverse = false;

    }

    var faces = THREE.ShapeUtils.triangulateShape( vertices, holes );

    // Vertices

    for ( i = 0, l = holes.length; i < l; i ++ ) {

        hole = holes[ i ];
        vertices = vertices.concat( hole );

    }

    //

    var vert, vlen = vertices.length;
    var face, flen = faces.length;

    for ( i = 0; i < vlen; i ++ ) {

        vert = vertices[ i ];

        this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) );

    }

    for ( i = 0; i < flen; i ++ ) {

        face = faces[ i ];

        var a = face[ 0 ] + shapesOffset;
        var b = face[ 1 ] + shapesOffset;
        var c = face[ 2 ] + shapesOffset;

        this.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
        this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) );

    }

};

// File:src/extras/geometries/LatheGeometry.js

/**
 * @author astrodud / http://astrodud.isgreat.org/
 * @author zz85 / https://github.com/zz85
 * @author bhouston / http://clara.io
 */

// points - to create a closed torus, one must use a set of points
//    like so: [ a, b, c, d, a ], see first is the same as last.
// segments - the number of circumference segments to create
// phiStart - the starting radian
// phiLength - the radian (0 to 2*PI) range of the lathed section
//    2*pi is a closed lathe, less than 2PI is a portion.

THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) {

    THREE.Geometry.call( this );

    this.type = 'LatheGeometry';

    this.parameters = {
        points: points,
        segments: segments,
        phiStart: phiStart,
        phiLength: phiLength
    };

    segments = segments || 12;
    phiStart = phiStart || 0;
    phiLength = phiLength || 2 * Math.PI;

    var inversePointLength = 1.0 / ( points.length - 1 );
    var inverseSegments = 1.0 / segments;

    for ( var i = 0, il = segments; i <= il; i ++ ) {

        var phi = phiStart + i * inverseSegments * phiLength;

        var c = Math.cos( phi ),
            s = Math.sin( phi );

        for ( var j = 0, jl = points.length; j < jl; j ++ ) {

            var pt = points[ j ];

            var vertex = new THREE.Vector3();

            vertex.x = c * pt.x - s * pt.y;
            vertex.y = s * pt.x + c * pt.y;
            vertex.z = pt.z;

            this.vertices.push( vertex );

        }

    }

    var np = points.length;

    for ( var i = 0, il = segments; i < il; i ++ ) {

        for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) {

            var base = j + np * i;
            var a = base;
            var b = base + np;
            var c = base + 1 + np;
            var d = base + 1;

            var u0 = i * inverseSegments;
            var v0 = j * inversePointLength;
            var u1 = u0 + inverseSegments;
            var v1 = v0 + inversePointLength;

            this.faces.push( new THREE.Face3( a, b, d ) );

            this.faceVertexUvs[ 0 ].push( [

                new THREE.Vector2( u0, v0 ),
                new THREE.Vector2( u1, v0 ),
                new THREE.Vector2( u0, v1 )

            ] );

            this.faces.push( new THREE.Face3( b, c, d ) );

            this.faceVertexUvs[ 0 ].push( [

                new THREE.Vector2( u1, v0 ),
                new THREE.Vector2( u1, v1 ),
                new THREE.Vector2( u0, v1 )

            ] );


        }

    }

    this.mergeVertices();
    this.computeFaceNormals();
    this.computeVertexNormals();

};

THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.LatheGeometry.prototype.constructor = THREE.LatheGeometry;

// File:src/extras/geometries/PlaneGeometry.js

/**
 * @author mrdoob / http://mrdoob.com/
 * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
 */

THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) {

    THREE.Geometry.call( this );

    this.type = 'PlaneGeometry';

    this.parameters = {
        width: width,
        height: height,
        widthSegments: widthSegments,
        heightSegments: heightSegments
    };

    this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );

};

THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.PlaneGeometry.prototype.constructor = THREE.PlaneGeometry;

THREE.PlaneGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.PlaneGeometry(
        parameters.width,
        parameters.height,
        parameters.widthSegments,
        parameters.heightSegments
    );

};

// File:src/extras/geometries/PlaneBufferGeometry.js

/**
 * @author mrdoob / http://mrdoob.com/
 * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
 */

THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) {

    THREE.BufferGeometry.call( this );

    this.type = 'PlaneBufferGeometry';

    this.parameters = {
        width: width,
        height: height,
        widthSegments: widthSegments,
        heightSegments: heightSegments
    };

    var width_half = width / 2;
    var height_half = height / 2;

    var gridX = Math.floor( widthSegments ) || 1;
    var gridY = Math.floor( heightSegments ) || 1;

    var gridX1 = gridX + 1;
    var gridY1 = gridY + 1;

    var segment_width = width / gridX;
    var segment_height = height / gridY;

    var vertices = new Float32Array( gridX1 * gridY1 * 3 );
    var normals = new Float32Array( gridX1 * gridY1 * 3 );
    var uvs = new Float32Array( gridX1 * gridY1 * 2 );

    var offset = 0;
    var offset2 = 0;

    for ( var iy = 0; iy < gridY1; iy ++ ) {

        var y = iy * segment_height - height_half;

        for ( var ix = 0; ix < gridX1; ix ++ ) {

            var x = ix * segment_width - width_half;

            vertices[ offset ] = x;
            vertices[ offset + 1 ] = - y;

            normals[ offset + 2 ] = 1;

            uvs[ offset2 ] = ix / gridX;
            uvs[ offset2 + 1 ] = 1 - ( iy / gridY );

            offset += 3;
            offset2 += 2;

        }

    }

    offset = 0;

    var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 );

    for ( var iy = 0; iy < gridY; iy ++ ) {

        for ( var ix = 0; ix < gridX; ix ++ ) {

            var a = ix + gridX1 * iy;
            var b = ix + gridX1 * ( iy + 1 );
            var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
            var d = ( ix + 1 ) + gridX1 * iy;

            indices[ offset ] = a;
            indices[ offset + 1 ] = b;
            indices[ offset + 2 ] = d;

            indices[ offset + 3 ] = b;
            indices[ offset + 4 ] = c;
            indices[ offset + 5 ] = d;

            offset += 6;

        }

    }

    this.setIndex( new THREE.BufferAttribute( indices, 1 ) );
    this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
    this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
    this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );

};

THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
THREE.PlaneBufferGeometry.prototype.constructor = THREE.PlaneBufferGeometry;

THREE.PlaneBufferGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.PlaneBufferGeometry(
        parameters.width,
        parameters.height,
        parameters.widthSegments,
        parameters.heightSegments
    );

};

// File:src/extras/geometries/RingGeometry.js

/**
 * @author Kaleb Murphy
 */

THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {

    THREE.Geometry.call( this );

    this.type = 'RingGeometry';

    this.parameters = {
        innerRadius: innerRadius,
        outerRadius: outerRadius,
        thetaSegments: thetaSegments,
        phiSegments: phiSegments,
        thetaStart: thetaStart,
        thetaLength: thetaLength
    };

    innerRadius = innerRadius || 0;
    outerRadius = outerRadius || 50;

    thetaStart = thetaStart !== undefined ? thetaStart : 0;
    thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;

    thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;
    phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8;

    var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );

    for ( i = 0; i < phiSegments + 1; i ++ ) {

        // concentric circles inside ring

        for ( o = 0; o < thetaSegments + 1; o ++ ) {

            // number of segments per circle

            var vertex = new THREE.Vector3();
            var segment = thetaStart + o / thetaSegments * thetaLength;
            vertex.x = radius * Math.cos( segment );
            vertex.y = radius * Math.sin( segment );

            this.vertices.push( vertex );
            uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) );

        }

        radius += radiusStep;

    }

    var n = new THREE.Vector3( 0, 0, 1 );

    for ( i = 0; i < phiSegments; i ++ ) {

        // concentric circles inside ring

        var thetaSegment = i * ( thetaSegments + 1 );

        for ( o = 0; o < thetaSegments ; o ++ ) {

            // number of segments per circle

            var segment = o + thetaSegment;

            var v1 = segment;
            var v2 = segment + thetaSegments + 1;
            var v3 = segment + thetaSegments + 2;

            this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
            this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] );

            v1 = segment;
            v2 = segment + thetaSegments + 2;
            v3 = segment + 1;

            this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) );
            this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ] );

        }

    }

    this.computeFaceNormals();

    this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );

};

THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.RingGeometry.prototype.constructor = THREE.RingGeometry;

THREE.RingGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.RingGeometry(
        parameters.innerRadius,
        parameters.outerRadius,
        parameters.thetaSegments,
        parameters.phiSegments,
        parameters.thetaStart,
        parameters.thetaLength
    );

};

// File:src/extras/geometries/SphereGeometry.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {

    THREE.Geometry.call( this );

    this.type = 'SphereGeometry';

    this.parameters = {
        radius: radius,
        widthSegments: widthSegments,
        heightSegments: heightSegments,
        phiStart: phiStart,
        phiLength: phiLength,
        thetaStart: thetaStart,
        thetaLength: thetaLength
    };

    this.fromBufferGeometry( new THREE.SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) );

};

THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry;

THREE.SphereGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.SphereGeometry(
        parameters.radius,
        parameters.widthSegments,
        parameters.heightSegments,
        parameters.phiStart,
        parameters.phiLength,
        parameters.thetaStart,
        parameters.thetaLength
    );

};

// File:src/extras/geometries/SphereBufferGeometry.js

/**
 * @author benaadams / https://twitter.com/ben_a_adams
 * based on THREE.SphereGeometry
 */

THREE.SphereBufferGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {

    THREE.BufferGeometry.call( this );

    this.type = 'SphereBufferGeometry';

    this.parameters = {
        radius: radius,
        widthSegments: widthSegments,
        heightSegments: heightSegments,
        phiStart: phiStart,
        phiLength: phiLength,
        thetaStart: thetaStart,
        thetaLength: thetaLength
    };

    radius = radius || 50;

    widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );
    heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );

    phiStart = phiStart !== undefined ? phiStart : 0;
    phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;

    thetaStart = thetaStart !== undefined ? thetaStart : 0;
    thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;

    var thetaEnd = thetaStart + thetaLength;

    var vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) );

    var positions = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );
    var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );
    var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 );

    var index = 0, vertices = [], normal = new THREE.Vector3();

    for ( var y = 0; y <= heightSegments; y ++ ) {

        var verticesRow = [];

        var v = y / heightSegments;

        for ( var x = 0; x <= widthSegments; x ++ ) {

            var u = x / widthSegments;

            var px = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
            var py = radius * Math.cos( thetaStart + v * thetaLength );
            var pz = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );

            normal.set( px, py, pz ).normalize();

            positions.setXYZ( index, px, py, pz );
            normals.setXYZ( index, normal.x, normal.y, normal.z );
            uvs.setXY( index, u, 1 - v );

            verticesRow.push( index );

            index ++;

        }

        vertices.push( verticesRow );

    }

    var indices = [];

    for ( var y = 0; y < heightSegments; y ++ ) {

        for ( var x = 0; x < widthSegments; x ++ ) {

            var v1 = vertices[ y ][ x + 1 ];
            var v2 = vertices[ y ][ x ];
            var v3 = vertices[ y + 1 ][ x ];
            var v4 = vertices[ y + 1 ][ x + 1 ];

            if ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 );
            if ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 );

        }

    }

    this.setIndex( new ( positions.count > 65535 ? THREE.Uint32Attribute : THREE.Uint16Attribute )( indices, 1 ) );
    this.addAttribute( 'position', positions );
    this.addAttribute( 'normal', normals );
    this.addAttribute( 'uv', uvs );

    this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );

};

THREE.SphereBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
THREE.SphereBufferGeometry.prototype.constructor = THREE.SphereBufferGeometry;

THREE.SphereBufferGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.SphereBufferGeometry(
        parameters.radius,
        parameters.widthSegments,
        parameters.heightSegments,
        parameters.phiStart,
        parameters.phiLength,
        parameters.thetaStart,
        parameters.thetaLength
    );

};

// File:src/extras/geometries/TorusGeometry.js

/**
 * @author oosmoxiecode
 * @author mrdoob / http://mrdoob.com/
 * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888
 */

THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) {

    THREE.Geometry.call( this );

    this.type = 'TorusGeometry';

    this.parameters = {
        radius: radius,
        tube: tube,
        radialSegments: radialSegments,
        tubularSegments: tubularSegments,
        arc: arc
    };

    radius = radius || 100;
    tube = tube || 40;
    radialSegments = radialSegments || 8;
    tubularSegments = tubularSegments || 6;
    arc = arc || Math.PI * 2;

    var center = new THREE.Vector3(), uvs = [], normals = [];

    for ( var j = 0; j <= radialSegments; j ++ ) {

        for ( var i = 0; i <= tubularSegments; i ++ ) {

            var u = i / tubularSegments * arc;
            var v = j / radialSegments * Math.PI * 2;

            center.x = radius * Math.cos( u );
            center.y = radius * Math.sin( u );

            var vertex = new THREE.Vector3();
            vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );
            vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );
            vertex.z = tube * Math.sin( v );

            this.vertices.push( vertex );

            uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) );
            normals.push( vertex.clone().sub( center ).normalize() );

        }

    }

    for ( var j = 1; j <= radialSegments; j ++ ) {

        for ( var i = 1; i <= tubularSegments; i ++ ) {

            var a = ( tubularSegments + 1 ) * j + i - 1;
            var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;
            var c = ( tubularSegments + 1 ) * ( j - 1 ) + i;
            var d = ( tubularSegments + 1 ) * j + i;

            var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] );
            this.faces.push( face );
            this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] );

            face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] );
            this.faces.push( face );
            this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] );

        }

    }

    this.computeFaceNormals();

};

THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.TorusGeometry.prototype.constructor = THREE.TorusGeometry;

THREE.TorusGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.TorusGeometry(
        parameters.radius,
        parameters.tube,
        parameters.radialSegments,
        parameters.tubularSegments,
        parameters.arc
    );

};

// File:src/extras/geometries/TorusKnotGeometry.js

/**
 * @author oosmoxiecode
 * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473
 */

THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) {

    THREE.Geometry.call( this );

    this.type = 'TorusKnotGeometry';

    this.parameters = {
        radius: radius,
        tube: tube,
        radialSegments: radialSegments,
        tubularSegments: tubularSegments,
        p: p,
        q: q,
        heightScale: heightScale
    };

    radius = radius || 100;
    tube = tube || 40;
    radialSegments = radialSegments || 64;
    tubularSegments = tubularSegments || 8;
    p = p || 2;
    q = q || 3;
    heightScale = heightScale || 1;

    var grid = new Array( radialSegments );
    var tang = new THREE.Vector3();
    var n = new THREE.Vector3();
    var bitan = new THREE.Vector3();

    for ( var i = 0; i < radialSegments; ++ i ) {

        grid[ i ] = new Array( tubularSegments );
        var u = i / radialSegments * 2 * p * Math.PI;
        var p1 = getPos( u, q, p, radius, heightScale );
        var p2 = getPos( u + 0.01, q, p, radius, heightScale );
        tang.subVectors( p2, p1 );
        n.addVectors( p2, p1 );

        bitan.crossVectors( tang, n );
        n.crossVectors( bitan, tang );
        bitan.normalize();
        n.normalize();

        for ( var j = 0; j < tubularSegments; ++ j ) {

            var v = j / tubularSegments * 2 * Math.PI;
            var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
            var cy = tube * Math.sin( v );

            var pos = new THREE.Vector3();
            pos.x = p1.x + cx * n.x + cy * bitan.x;
            pos.y = p1.y + cx * n.y + cy * bitan.y;
            pos.z = p1.z + cx * n.z + cy * bitan.z;

            grid[ i ][ j ] = this.vertices.push( pos ) - 1;

        }

    }

    for ( var i = 0; i < radialSegments; ++ i ) {

        for ( var j = 0; j < tubularSegments; ++ j ) {

            var ip = ( i + 1 ) % radialSegments;
            var jp = ( j + 1 ) % tubularSegments;

            var a = grid[ i ][ j ];
            var b = grid[ ip ][ j ];
            var c = grid[ ip ][ jp ];
            var d = grid[ i ][ jp ];

            var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments );
            var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments );
            var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments );
            var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments );

            this.faces.push( new THREE.Face3( a, b, d ) );
            this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );

            this.faces.push( new THREE.Face3( b, c, d ) );
            this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );

        }

    }

    this.computeFaceNormals();
    this.computeVertexNormals();

    function getPos( u, in_q, in_p, radius, heightScale ) {

        var cu = Math.cos( u );
        var su = Math.sin( u );
        var quOverP = in_q / in_p * u;
        var cs = Math.cos( quOverP );

        var tx = radius * ( 2 + cs ) * 0.5 * cu;
        var ty = radius * ( 2 + cs ) * su * 0.5;
        var tz = heightScale * radius * Math.sin( quOverP ) * 0.5;

        return new THREE.Vector3( tx, ty, tz );

    }

};

THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.TorusKnotGeometry.prototype.constructor = THREE.TorusKnotGeometry;

THREE.TorusKnotGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.TorusKnotGeometry(
        parameters.radius,
        parameters.tube,
        parameters.radialSegments,
        parameters.tubularSegments,
        parameters.p,
        parameters.q,
        parameters.heightScale
    );

};

// File:src/extras/geometries/TubeGeometry.js

/**
 * @author WestLangley / https://github.com/WestLangley
 * @author zz85 / https://github.com/zz85
 * @author miningold / https://github.com/miningold
 * @author jonobr1 / https://github.com/jonobr1
 *
 * Modified from the TorusKnotGeometry by @oosmoxiecode
 *
 * Creates a tube which extrudes along a 3d spline
 *
 * Uses parallel transport frames as described in
 * http://www.cs.indiana.edu/pub/techreports/TR425.pdf
 */

THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, taper ) {

    THREE.Geometry.call( this );

    this.type = 'TubeGeometry';

    this.parameters = {
        path: path,
        segments: segments,
        radius: radius,
        radialSegments: radialSegments,
        closed: closed,
        taper: taper
    };

    segments = segments || 64;
    radius = radius || 1;
    radialSegments = radialSegments || 8;
    closed = closed || false;
    taper = taper || THREE.TubeGeometry.NoTaper;

    var grid = [];

    var scope = this,

        tangent,
        normal,
        binormal,

        numpoints = segments + 1,

        u, v, r,

        cx, cy,
        pos, pos2 = new THREE.Vector3(),
        i, j,
        ip, jp,
        a, b, c, d,
        uva, uvb, uvc, uvd;

    var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ),
        tangents = frames.tangents,
        normals = frames.normals,
        binormals = frames.binormals;

    // proxy internals
    this.tangents = tangents;
    this.normals = normals;
    this.binormals = binormals;

    function vert( x, y, z ) {

        return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;

    }

    // construct the grid

    for ( i = 0; i < numpoints; i ++ ) {

        grid[ i ] = [];

        u = i / ( numpoints - 1 );

        pos = path.getPointAt( u );

        tangent = tangents[ i ];
        normal = normals[ i ];
        binormal = binormals[ i ];

        r = radius * taper( u );

        for ( j = 0; j < radialSegments; j ++ ) {

            v = j / radialSegments * 2 * Math.PI;

            cx = - r * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
            cy = r * Math.sin( v );

            pos2.copy( pos );
            pos2.x += cx * normal.x + cy * binormal.x;
            pos2.y += cx * normal.y + cy * binormal.y;
            pos2.z += cx * normal.z + cy * binormal.z;

            grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );

        }

    }


    // construct the mesh

    for ( i = 0; i < segments; i ++ ) {

        for ( j = 0; j < radialSegments; j ++ ) {

            ip = ( closed ) ? ( i + 1 ) % segments : i + 1;
            jp = ( j + 1 ) % radialSegments;

            a = grid[ i ][ j ];		// *** NOT NECESSARILY PLANAR ! ***
            b = grid[ ip ][ j ];
            c = grid[ ip ][ jp ];
            d = grid[ i ][ jp ];

            uva = new THREE.Vector2( i / segments, j / radialSegments );
            uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments );
            uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments );
            uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments );

            this.faces.push( new THREE.Face3( a, b, d ) );
            this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );

            this.faces.push( new THREE.Face3( b, c, d ) );
            this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );

        }

    }

    this.computeFaceNormals();
    this.computeVertexNormals();

};

THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.TubeGeometry.prototype.constructor = THREE.TubeGeometry;
THREE.TubeGeometry.prototype.clone = function() {

    return new this.constructor( this.parameters.path,
        this.parameters.segments, this.parameters.radius, this.parameters.radialSegments,
        this.parameters.closed, this.parameters.taper
    );

};

THREE.TubeGeometry.NoTaper = function ( u ) {

    return 1;

};

THREE.TubeGeometry.SinusoidalTaper = function ( u ) {

    return Math.sin( Math.PI * u );

};

// For computing of Frenet frames, exposing the tangents, normals and binormals the spline
THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) {

    var	normal = new THREE.Vector3(),

        tangents = [],
        normals = [],
        binormals = [],

        vec = new THREE.Vector3(),
        mat = new THREE.Matrix4(),

        numpoints = segments + 1,
        theta,
        smallest,

        tx, ty, tz,
        i, u;


    // expose internals
    this.tangents = tangents;
    this.normals = normals;
    this.binormals = binormals;

    // compute the tangent vectors for each segment on the path

    for ( i = 0; i < numpoints; i ++ ) {

        u = i / ( numpoints - 1 );

        tangents[ i ] = path.getTangentAt( u );
        tangents[ i ].normalize();

    }

    initialNormal3();

    /*
	function initialNormal1(lastBinormal) {
		// fixed start binormal. Has dangers of 0 vectors
		normals[ 0 ] = new THREE.Vector3();
		binormals[ 0 ] = new THREE.Vector3();
		if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 );
		normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize();
		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
	}

	function initialNormal2() {

		// This uses the Frenet-Serret formula for deriving binormal
		var t2 = path.getTangentAt( epsilon );

		normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize();
		binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] );

		normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent
		binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();

	}
	*/

    function initialNormal3() {

        // select an initial normal vector perpendicular to the first tangent vector,
        // and in the direction of the smallest tangent xyz component

        normals[ 0 ] = new THREE.Vector3();
        binormals[ 0 ] = new THREE.Vector3();
        smallest = Number.MAX_VALUE;
        tx = Math.abs( tangents[ 0 ].x );
        ty = Math.abs( tangents[ 0 ].y );
        tz = Math.abs( tangents[ 0 ].z );

        if ( tx <= smallest ) {

            smallest = tx;
            normal.set( 1, 0, 0 );

        }

        if ( ty <= smallest ) {

            smallest = ty;
            normal.set( 0, 1, 0 );

        }

        if ( tz <= smallest ) {

            normal.set( 0, 0, 1 );

        }

        vec.crossVectors( tangents[ 0 ], normal ).normalize();

        normals[ 0 ].crossVectors( tangents[ 0 ], vec );
        binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );

    }


    // compute the slowly-varying normal and binormal vectors for each segment on the path

    for ( i = 1; i < numpoints; i ++ ) {

        normals[ i ] = normals[ i - 1 ].clone();

        binormals[ i ] = binormals[ i - 1 ].clone();

        vec.crossVectors( tangents[ i - 1 ], tangents[ i ] );

        if ( vec.length() > Number.EPSILON ) {

            vec.normalize();

            theta = Math.acos( THREE.Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors

            normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );

        }

        binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );

    }


    // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same

    if ( closed ) {

        theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints - 1 ] ), - 1, 1 ) );
        theta /= ( numpoints - 1 );

        if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints - 1 ] ) ) > 0 ) {

            theta = - theta;

        }

        for ( i = 1; i < numpoints; i ++ ) {

            // twist a little...
            normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
            binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );

        }

    }

};

// File:src/extras/geometries/PolyhedronGeometry.js

/**
 * @author clockworkgeek / https://github.com/clockworkgeek
 * @author timothypratley / https://github.com/timothypratley
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) {

    THREE.Geometry.call( this );

    this.type = 'PolyhedronGeometry';

    this.parameters = {
        vertices: vertices,
        indices: indices,
        radius: radius,
        detail: detail
    };

    radius = radius || 1;
    detail = detail || 0;

    var that = this;

    for ( var i = 0, l = vertices.length; i < l; i += 3 ) {

        prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) );

    }

    var p = this.vertices;

    var faces = [];

    for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) {

        var v1 = p[ indices[ i ] ];
        var v2 = p[ indices[ i + 1 ] ];
        var v3 = p[ indices[ i + 2 ] ];

        faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ], undefined, j );

    }

    var centroid = new THREE.Vector3();

    for ( var i = 0, l = faces.length; i < l; i ++ ) {

        subdivide( faces[ i ], detail );

    }


    // Handle case when face straddles the seam

    for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) {

        var uvs = this.faceVertexUvs[ 0 ][ i ];

        var x0 = uvs[ 0 ].x;
        var x1 = uvs[ 1 ].x;
        var x2 = uvs[ 2 ].x;

        var max = Math.max( x0, x1, x2 );
        var min = Math.min( x0, x1, x2 );

        if ( max > 0.9 && min < 0.1 ) {

            // 0.9 is somewhat arbitrary

            if ( x0 < 0.2 ) uvs[ 0 ].x += 1;
            if ( x1 < 0.2 ) uvs[ 1 ].x += 1;
            if ( x2 < 0.2 ) uvs[ 2 ].x += 1;

        }

    }


    // Apply radius

    for ( var i = 0, l = this.vertices.length; i < l; i ++ ) {

        this.vertices[ i ].multiplyScalar( radius );

    }


    // Merge vertices

    this.mergeVertices();

    this.computeFaceNormals();

    this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );


    // Project vector onto sphere's surface

    function prepare( vector ) {

        var vertex = vector.normalize().clone();
        vertex.index = that.vertices.push( vertex ) - 1;

        // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.

        var u = azimuth( vector ) / 2 / Math.PI + 0.5;
        var v = inclination( vector ) / Math.PI + 0.5;
        vertex.uv = new THREE.Vector2( u, 1 - v );

        return vertex;

    }


    // Approximate a curved face with recursively sub-divided triangles.

    function make( v1, v2, v3, materialIndex ) {

        var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ], undefined, materialIndex );
        that.faces.push( face );

        centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 );

        var azi = azimuth( centroid );

        that.faceVertexUvs[ 0 ].push( [
            correctUV( v1.uv, v1, azi ),
            correctUV( v2.uv, v2, azi ),
            correctUV( v3.uv, v3, azi )
        ] );

    }


    // Analytically subdivide a face to the required detail level.

    function subdivide( face, detail ) {

        var cols = Math.pow( 2, detail );
        var a = prepare( that.vertices[ face.a ] );
        var b = prepare( that.vertices[ face.b ] );
        var c = prepare( that.vertices[ face.c ] );
        var v = [];

        var materialIndex = face.materialIndex;

        // Construct all of the vertices for this subdivision.

        for ( var i = 0 ; i <= cols; i ++ ) {

            v[ i ] = [];

            var aj = prepare( a.clone().lerp( c, i / cols ) );
            var bj = prepare( b.clone().lerp( c, i / cols ) );
            var rows = cols - i;

            for ( var j = 0; j <= rows; j ++ ) {

                if ( j === 0 && i === cols ) {

                    v[ i ][ j ] = aj;

                } else {

                    v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) );

                }

            }

        }

        // Construct all of the faces.

        for ( var i = 0; i < cols ; i ++ ) {

            for ( var j = 0; j < 2 * ( cols - i ) - 1; j ++ ) {

                var k = Math.floor( j / 2 );

                if ( j % 2 === 0 ) {

                    make(
                        v[ i ][ k + 1 ],
                        v[ i + 1 ][ k ],
                        v[ i ][ k ],
                        materialIndex
                    );

                } else {

                    make(
                        v[ i ][ k + 1 ],
                        v[ i + 1 ][ k + 1 ],
                        v[ i + 1 ][ k ],
                        materialIndex
                    );

                }

            }

        }

    }


    // Angle around the Y axis, counter-clockwise when looking from above.

    function azimuth( vector ) {

        return Math.atan2( vector.z, - vector.x );

    }


    // Angle above the XZ plane.

    function inclination( vector ) {

        return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );

    }


    // Texture fixing helper. Spheres have some odd behaviours.

    function correctUV( uv, vector, azimuth ) {

        if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y );
        if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y );
        return uv.clone();

    }


};

THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.PolyhedronGeometry.prototype.constructor = THREE.PolyhedronGeometry;

THREE.PolyhedronGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.PolyhedronGeometry(
        parameters.vertices,
        parameters.indices,
        parameters.radius,
        parameters.detail
    );

};

// File:src/extras/geometries/DodecahedronGeometry.js

/**
 * @author Abe Pazos / https://hamoid.com
 */

THREE.DodecahedronGeometry = function ( radius, detail ) {

    var t = ( 1 + Math.sqrt( 5 ) ) / 2;
    var r = 1 / t;

    var vertices = [

        // (±1, ±1, ±1)
        - 1, - 1, - 1,    - 1, - 1,  1,
        - 1,  1, - 1,    - 1,  1,  1,
        1, - 1, - 1,     1, - 1,  1,
        1,  1, - 1,     1,  1,  1,

        // (0, ±1/φ, ±φ)
        0, - r, - t,     0, - r,  t,
        0,  r, - t,     0,  r,  t,

        // (±1/φ, ±φ, 0)
        - r, - t,  0,    - r,  t,  0,
        r, - t,  0,     r,  t,  0,

        // (±φ, 0, ±1/φ)
        - t,  0, - r,     t,  0, - r,
        - t,  0,  r,     t,  0,  r
    ];

    var indices = [
        3, 11,  7,      3,  7, 15,      3, 15, 13,
        7, 19, 17,      7, 17,  6,      7,  6, 15,
        17,  4,  8,     17,  8, 10,     17, 10,  6,
        8,  0, 16,      8, 16,  2,      8,  2, 10,
        0, 12,  1,      0,  1, 18,      0, 18, 16,
        6, 10,  2,      6,  2, 13,      6, 13, 15,
        2, 16, 18,      2, 18,  3,      2,  3, 13,
        18,  1,  9,     18,  9, 11,     18, 11,  3,
        4, 14, 12,      4, 12,  0,      4,  0,  8,
        11,  9,  5,     11,  5, 19,     11, 19,  7,
        19,  5, 14,     19, 14,  4,     19,  4, 17,
        1, 12, 14,      1, 14,  5,      1,  5,  9
    ];

    THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );

    this.type = 'DodecahedronGeometry';

    this.parameters = {
        radius: radius,
        detail: detail
    };

};

THREE.DodecahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype );
THREE.DodecahedronGeometry.prototype.constructor = THREE.DodecahedronGeometry;

THREE.DodecahedronGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.DodecahedronGeometry(
        parameters.radius,
        parameters.detail
    );

};

// File:src/extras/geometries/IcosahedronGeometry.js

/**
 * @author timothypratley / https://github.com/timothypratley
 */

THREE.IcosahedronGeometry = function ( radius, detail ) {

    var t = ( 1 + Math.sqrt( 5 ) ) / 2;

    var vertices = [
        - 1,  t,  0,    1,  t,  0,   - 1, - t,  0,    1, - t,  0,
        0, - 1,  t,    0,  1,  t,    0, - 1, - t,    0,  1, - t,
        t,  0, - 1,    t,  0,  1,   - t,  0, - 1,   - t,  0,  1
    ];

    var indices = [
        0, 11,  5,    0,  5,  1,    0,  1,  7,    0,  7, 10,    0, 10, 11,
        1,  5,  9,    5, 11,  4,   11, 10,  2,   10,  7,  6,    7,  1,  8,
        3,  9,  4,    3,  4,  2,    3,  2,  6,    3,  6,  8,    3,  8,  9,
        4,  9,  5,    2,  4, 11,    6,  2, 10,    8,  6,  7,    9,  8,  1
    ];

    THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );

    this.type = 'IcosahedronGeometry';

    this.parameters = {
        radius: radius,
        detail: detail
    };

};

THREE.IcosahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype );
THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry;

THREE.IcosahedronGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.IcosahedronGeometry(
        parameters.radius,
        parameters.detail
    );

};

// File:src/extras/geometries/OctahedronGeometry.js

/**
 * @author timothypratley / https://github.com/timothypratley
 */

THREE.OctahedronGeometry = function ( radius, detail ) {

    var vertices = [
        1, 0, 0,   - 1, 0, 0,    0, 1, 0,    0, - 1, 0,    0, 0, 1,    0, 0, - 1
    ];

    var indices = [
        0, 2, 4,    0, 4, 3,    0, 3, 5,    0, 5, 2,    1, 2, 5,    1, 5, 3,    1, 3, 4,    1, 4, 2
    ];

    THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );

    this.type = 'OctahedronGeometry';

    this.parameters = {
        radius: radius,
        detail: detail
    };

};

THREE.OctahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype );
THREE.OctahedronGeometry.prototype.constructor = THREE.OctahedronGeometry;

THREE.OctahedronGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.OctahedronGeometry(
        parameters.radius,
        parameters.detail
    );

};

// File:src/extras/geometries/TetrahedronGeometry.js

/**
 * @author timothypratley / https://github.com/timothypratley
 */

THREE.TetrahedronGeometry = function ( radius, detail ) {

    var vertices = [
        1,  1,  1,   - 1, - 1,  1,   - 1,  1, - 1,    1, - 1, - 1
    ];

    var indices = [
        2,  1,  0,    0,  3,  2,    1,  3,  0,    2,  3,  1
    ];

    THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail );

    this.type = 'TetrahedronGeometry';

    this.parameters = {
        radius: radius,
        detail: detail
    };

};

THREE.TetrahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype );
THREE.TetrahedronGeometry.prototype.constructor = THREE.TetrahedronGeometry;

THREE.TetrahedronGeometry.prototype.clone = function () {

    var parameters = this.parameters;

    return new THREE.TetrahedronGeometry(
        parameters.radius,
        parameters.detail
    );

};

// File:src/extras/geometries/ParametricGeometry.js

/**
 * @author zz85 / https://github.com/zz85
 * Parametric Surfaces Geometry
 * based on the brilliant article by @prideout http://prideout.net/blog/?p=44
 *
 * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements );
 *
 */

THREE.ParametricGeometry = function ( func, slices, stacks ) {

    THREE.Geometry.call( this );

    this.type = 'ParametricGeometry';

    this.parameters = {
        func: func,
        slices: slices,
        stacks: stacks
    };

    var verts = this.vertices;
    var faces = this.faces;
    var uvs = this.faceVertexUvs[ 0 ];

    var i, j, p;
    var u, v;

    var sliceCount = slices + 1;

    for ( i = 0; i <= stacks; i ++ ) {

        v = i / stacks;

        for ( j = 0; j <= slices; j ++ ) {

            u = j / slices;

            p = func( u, v );
            verts.push( p );

        }

    }

    var a, b, c, d;
    var uva, uvb, uvc, uvd;

    for ( i = 0; i < stacks; i ++ ) {

        for ( j = 0; j < slices; j ++ ) {

            a = i * sliceCount + j;
            b = i * sliceCount + j + 1;
            c = ( i + 1 ) * sliceCount + j + 1;
            d = ( i + 1 ) * sliceCount + j;

            uva = new THREE.Vector2( j / slices, i / stacks );
            uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks );
            uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks );
            uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks );

            faces.push( new THREE.Face3( a, b, d ) );
            uvs.push( [ uva, uvb, uvd ] );

            faces.push( new THREE.Face3( b, c, d ) );
            uvs.push( [ uvb.clone(), uvc, uvd.clone() ] );

        }

    }

    // console.log(this);

    // magic bullet
    // var diff = this.mergeVertices();
    // console.log('removed ', diff, ' vertices by merging');

    this.computeFaceNormals();
    this.computeVertexNormals();

};

THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype );
THREE.ParametricGeometry.prototype.constructor = THREE.ParametricGeometry;

// File:src/extras/geometries/WireframeGeometry.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.WireframeGeometry = function ( geometry ) {

    THREE.BufferGeometry.call( this );

    var edge = [ 0, 0 ], hash = {};

    function sortFunction( a, b ) {

        return a - b;

    }

    var keys = [ 'a', 'b', 'c' ];

    if ( geometry instanceof THREE.Geometry ) {

        var vertices = geometry.vertices;
        var faces = geometry.faces;
        var numEdges = 0;

        // allocate maximal size
        var edges = new Uint32Array( 6 * faces.length );

        for ( var i = 0, l = faces.length; i < l; i ++ ) {

            var face = faces[ i ];

            for ( var j = 0; j < 3; j ++ ) {

                edge[ 0 ] = face[ keys[ j ] ];
                edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ];
                edge.sort( sortFunction );

                var key = edge.toString();

                if ( hash[ key ] === undefined ) {

                    edges[ 2 * numEdges ] = edge[ 0 ];
                    edges[ 2 * numEdges + 1 ] = edge[ 1 ];
                    hash[ key ] = true;
                    numEdges ++;

                }

            }

        }

        var coords = new Float32Array( numEdges * 2 * 3 );

        for ( var i = 0, l = numEdges; i < l; i ++ ) {

            for ( var j = 0; j < 2; j ++ ) {

                var vertex = vertices[ edges [ 2 * i + j ] ];

                var index = 6 * i + 3 * j;
                coords[ index + 0 ] = vertex.x;
                coords[ index + 1 ] = vertex.y;
                coords[ index + 2 ] = vertex.z;

            }

        }

        this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );

    } else if ( geometry instanceof THREE.BufferGeometry ) {

        if ( geometry.index !== null ) {

            // Indexed BufferGeometry

            var indices = geometry.index.array;
            var vertices = geometry.attributes.position;
            var drawcalls = geometry.drawcalls;
            var numEdges = 0;

            if ( drawcalls.length === 0 ) {

                geometry.addGroup( 0, indices.length );

            }

            // allocate maximal size
            var edges = new Uint32Array( 2 * indices.length );

            for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) {

                var drawcall = drawcalls[ o ];

                var start = drawcall.start;
                var count = drawcall.count;

                for ( var i = start, il = start + count; i < il; i += 3 ) {

                    for ( var j = 0; j < 3; j ++ ) {

                        edge[ 0 ] = indices[ i + j ];
                        edge[ 1 ] = indices[ i + ( j + 1 ) % 3 ];
                        edge.sort( sortFunction );

                        var key = edge.toString();

                        if ( hash[ key ] === undefined ) {

                            edges[ 2 * numEdges ] = edge[ 0 ];
                            edges[ 2 * numEdges + 1 ] = edge[ 1 ];
                            hash[ key ] = true;
                            numEdges ++;

                        }

                    }

                }

            }

            var coords = new Float32Array( numEdges * 2 * 3 );

            for ( var i = 0, l = numEdges; i < l; i ++ ) {

                for ( var j = 0; j < 2; j ++ ) {

                    var index = 6 * i + 3 * j;
                    var index2 = edges[ 2 * i + j ];

                    coords[ index + 0 ] = vertices.getX( index2 );
                    coords[ index + 1 ] = vertices.getY( index2 );
                    coords[ index + 2 ] = vertices.getZ( index2 );

                }

            }

            this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );

        } else {

            // non-indexed BufferGeometry

            var vertices = geometry.attributes.position.array;
            var numEdges = vertices.length / 3;
            var numTris = numEdges / 3;

            var coords = new Float32Array( numEdges * 2 * 3 );

            for ( var i = 0, l = numTris; i < l; i ++ ) {

                for ( var j = 0; j < 3; j ++ ) {

                    var index = 18 * i + 6 * j;

                    var index1 = 9 * i + 3 * j;
                    coords[ index + 0 ] = vertices[ index1 ];
                    coords[ index + 1 ] = vertices[ index1 + 1 ];
                    coords[ index + 2 ] = vertices[ index1 + 2 ];

                    var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 );
                    coords[ index + 3 ] = vertices[ index2 ];
                    coords[ index + 4 ] = vertices[ index2 + 1 ];
                    coords[ index + 5 ] = vertices[ index2 + 2 ];

                }

            }

            this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) );

        }

    }

};

THREE.WireframeGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
THREE.WireframeGeometry.prototype.constructor = THREE.WireframeGeometry;

// File:src/extras/helpers/AxisHelper.js

/**
 * @author sroucheray / http://sroucheray.org/
 * @author mrdoob / http://mrdoob.com/
 */

THREE.AxisHelper = function ( size ) {

    size = size || 1;

    var vertices = new Float32Array( [
        0, 0, 0,  size, 0, 0,
        0, 0, 0,  0, size, 0,
        0, 0, 0,  0, 0, size
    ] );

    var colors = new Float32Array( [
        1, 0, 0,  1, 0.6, 0,
        0, 1, 0,  0.6, 1, 0,
        0, 0, 1,  0, 0.6, 1
    ] );

    var geometry = new THREE.BufferGeometry();
    geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
    geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );

    var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );

    THREE.LineSegments.call( this, geometry, material );

};

THREE.AxisHelper.prototype = Object.create( THREE.LineSegments.prototype );
THREE.AxisHelper.prototype.constructor = THREE.AxisHelper;

// File:src/extras/helpers/ArrowHelper.js

/**
 * @author WestLangley / http://github.com/WestLangley
 * @author zz85 / http://github.com/zz85
 * @author bhouston / http://clara.io
 *
 * Creates an arrow for visualizing directions
 *
 * Parameters:
 *  dir - Vector3
 *  origin - Vector3
 *  length - Number
 *  color - color in hex value
 *  headLength - Number
 *  headWidth - Number
 */

THREE.ArrowHelper = ( function () {

    var lineGeometry = new THREE.Geometry();
    lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );

    var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 );
    coneGeometry.translate( 0, - 0.5, 0 );

    return function ArrowHelper( dir, origin, length, color, headLength, headWidth ) {

        // dir is assumed to be normalized

        THREE.Object3D.call( this );

        if ( color === undefined ) color = 0xffff00;
        if ( length === undefined ) length = 1;
        if ( headLength === undefined ) headLength = 0.2 * length;
        if ( headWidth === undefined ) headWidth = 0.2 * headLength;

        this.position.copy( origin );

        if ( headLength < length ) {
            this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) );
            this.line.matrixAutoUpdate = false;
            this.add( this.line );
        }

        this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) );
        this.cone.matrixAutoUpdate = false;
        this.add( this.cone );

        this.setDirection( dir );
        this.setLength( length, headLength, headWidth );

    }

}() );

THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype );
THREE.ArrowHelper.prototype.constructor = THREE.ArrowHelper;

THREE.ArrowHelper.prototype.setDirection = ( function () {

    var axis = new THREE.Vector3();
    var radians;

    return function setDirection( dir ) {

        // dir is assumed to be normalized

        if ( dir.y > 0.99999 ) {

            this.quaternion.set( 0, 0, 0, 1 );

        } else if ( dir.y < - 0.99999 ) {

            this.quaternion.set( 1, 0, 0, 0 );

        } else {

            axis.set( dir.z, 0, - dir.x ).normalize();

            radians = Math.acos( dir.y );

            this.quaternion.setFromAxisAngle( axis, radians );

        }

    };

}() );

THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) {

    if ( headLength === undefined ) headLength = 0.2 * length;
    if ( headWidth === undefined ) headWidth = 0.2 * headLength;

    if ( headLength < length ){
        this.line.scale.set( 1, length - headLength, 1 );
        this.line.updateMatrix();
    }

    this.cone.scale.set( headWidth, headLength, headWidth );
    this.cone.position.y = length;
    this.cone.updateMatrix();

};

THREE.ArrowHelper.prototype.setColor = function ( color ) {

    if ( this.line !== undefined ) this.line.material.color.set( color );
    this.cone.material.color.set( color );

};

// File:src/extras/helpers/BoxHelper.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.BoxHelper = function ( object ) {

    var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );
    var positions = new Float32Array( 8 * 3 );

    var geometry = new THREE.BufferGeometry();
    geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
    geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );

    THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ) );

    if ( object !== undefined ) {

        this.update( object );

    }

};

THREE.BoxHelper.prototype = Object.create( THREE.LineSegments.prototype );
THREE.BoxHelper.prototype.constructor = THREE.BoxHelper;

THREE.BoxHelper.prototype.update = ( function () {

    var box = new THREE.Box3();

    return function ( object ) {

        box.setFromObject( object );

        if ( box.empty() ) return;

        var min = box.min;
        var max = box.max;

        /*
		  5____4
		1/___0/|
		| 6__|_7
		2/___3/

		0: max.x, max.y, max.z
		1: min.x, max.y, max.z
		2: min.x, min.y, max.z
		3: max.x, min.y, max.z
		4: max.x, max.y, min.z
		5: min.x, max.y, min.z
		6: min.x, min.y, min.z
		7: max.x, min.y, min.z
		*/

        var position = this.geometry.attributes.position;
        var array = position.array;

        array[  0 ] = max.x; array[  1 ] = max.y; array[  2 ] = max.z;
        array[  3 ] = min.x; array[  4 ] = max.y; array[  5 ] = max.z;
        array[  6 ] = min.x; array[  7 ] = min.y; array[  8 ] = max.z;
        array[  9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z;
        array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z;
        array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z;
        array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z;
        array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z;

        position.needsUpdate = true;

        this.geometry.computeBoundingSphere();

    }

} )();

// File:src/extras/helpers/BoundingBoxHelper.js

/**
 * @author WestLangley / http://github.com/WestLangley
 */

// a helper to show the world-axis-aligned bounding box for an object

THREE.BoundingBoxHelper = function ( object, hex ) {

    var color = ( hex !== undefined ) ? hex : 0x888888;

    this.object = object;

    this.box = new THREE.Box3();

    THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) );

};

THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype );
THREE.BoundingBoxHelper.prototype.constructor = THREE.BoundingBoxHelper;

THREE.BoundingBoxHelper.prototype.update = function () {

    this.box.setFromObject( this.object );

    this.box.size( this.scale );

    this.box.center( this.position );

};

// File:src/extras/helpers/CameraHelper.js

/**
 * @author alteredq / http://alteredqualia.com/
 *
 *	- shows frustum, line of sight and up of the camera
 *	- suitable for fast updates
 * 	- based on frustum visualization in lightgl.js shadowmap example
 *		http://evanw.github.com/lightgl.js/tests/shadowmap.html
 */

THREE.CameraHelper = function ( camera ) {

    var geometry = new THREE.Geometry();
    var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } );

    var pointMap = {};

    // colors

    var hexFrustum = 0xffaa00;
    var hexCone = 0xff0000;
    var hexUp = 0x00aaff;
    var hexTarget = 0xffffff;
    var hexCross = 0x333333;

    // near

    addLine( "n1", "n2", hexFrustum );
    addLine( "n2", "n4", hexFrustum );
    addLine( "n4", "n3", hexFrustum );
    addLine( "n3", "n1", hexFrustum );

    // far

    addLine( "f1", "f2", hexFrustum );
    addLine( "f2", "f4", hexFrustum );
    addLine( "f4", "f3", hexFrustum );
    addLine( "f3", "f1", hexFrustum );

    // sides

    addLine( "n1", "f1", hexFrustum );
    addLine( "n2", "f2", hexFrustum );
    addLine( "n3", "f3", hexFrustum );
    addLine( "n4", "f4", hexFrustum );

    // cone

    addLine( "p", "n1", hexCone );
    addLine( "p", "n2", hexCone );
    addLine( "p", "n3", hexCone );
    addLine( "p", "n4", hexCone );

    // up

    addLine( "u1", "u2", hexUp );
    addLine( "u2", "u3", hexUp );
    addLine( "u3", "u1", hexUp );

    // target

    addLine( "c", "t", hexTarget );
    addLine( "p", "c", hexCross );

    // cross

    addLine( "cn1", "cn2", hexCross );
    addLine( "cn3", "cn4", hexCross );

    addLine( "cf1", "cf2", hexCross );
    addLine( "cf3", "cf4", hexCross );

    function addLine( a, b, hex ) {

        addPoint( a, hex );
        addPoint( b, hex );

    }

    function addPoint( id, hex ) {

        geometry.vertices.push( new THREE.Vector3() );
        geometry.colors.push( new THREE.Color( hex ) );

        if ( pointMap[ id ] === undefined ) {

            pointMap[ id ] = [];

        }

        pointMap[ id ].push( geometry.vertices.length - 1 );

    }

    THREE.LineSegments.call( this, geometry, material );

    this.camera = camera;
    this.camera.updateProjectionMatrix();

    this.matrix = camera.matrixWorld;
    this.matrixAutoUpdate = false;

    this.pointMap = pointMap;

    this.update();

};

THREE.CameraHelper.prototype = Object.create( THREE.LineSegments.prototype );
THREE.CameraHelper.prototype.constructor = THREE.CameraHelper;

THREE.CameraHelper.prototype.update = function () {

    var geometry, pointMap;

    var vector = new THREE.Vector3();
    var camera = new THREE.Camera();

    function setPoint( point, x, y, z ) {

        vector.set( x, y, z ).unproject( camera );

        var points = pointMap[ point ];

        if ( points !== undefined ) {

            for ( var i = 0, il = points.length; i < il; i ++ ) {

                geometry.vertices[ points[ i ] ].copy( vector );

            }

        }

    }

    return function () {

        geometry = this.geometry;
        pointMap = this.pointMap;

        var w = 1, h = 1;

        // we need just camera projection matrix
        // world matrix must be identity

        camera.projectionMatrix.copy( this.camera.projectionMatrix );

        // center / target

        setPoint( "c", 0, 0, - 1 );
        setPoint( "t", 0, 0,  1 );

        // near

        setPoint( "n1", - w, - h, - 1 );
        setPoint( "n2",   w, - h, - 1 );
        setPoint( "n3", - w,   h, - 1 );
        setPoint( "n4",   w,   h, - 1 );

        // far

        setPoint( "f1", - w, - h, 1 );
        setPoint( "f2",   w, - h, 1 );
        setPoint( "f3", - w,   h, 1 );
        setPoint( "f4",   w,   h, 1 );

        // up

        setPoint( "u1",   w * 0.7, h * 1.1, - 1 );
        setPoint( "u2", - w * 0.7, h * 1.1, - 1 );
        setPoint( "u3",         0, h * 2,   - 1 );

        // cross

        setPoint( "cf1", - w,   0, 1 );
        setPoint( "cf2",   w,   0, 1 );
        setPoint( "cf3",   0, - h, 1 );
        setPoint( "cf4",   0,   h, 1 );

        setPoint( "cn1", - w,   0, - 1 );
        setPoint( "cn2",   w,   0, - 1 );
        setPoint( "cn3",   0, - h, - 1 );
        setPoint( "cn4",   0,   h, - 1 );

        geometry.verticesNeedUpdate = true;

    };

}();

// File:src/extras/helpers/DirectionalLightHelper.js

/**
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.DirectionalLightHelper = function ( light, size ) {

    THREE.Object3D.call( this );

    this.light = light;
    this.light.updateMatrixWorld();

    this.matrix = light.matrixWorld;
    this.matrixAutoUpdate = false;

    size = size || 1;

    var geometry = new THREE.Geometry();
    geometry.vertices.push(
        new THREE.Vector3( - size,   size, 0 ),
        new THREE.Vector3(   size,   size, 0 ),
        new THREE.Vector3(   size, - size, 0 ),
        new THREE.Vector3( - size, - size, 0 ),
        new THREE.Vector3( - size,   size, 0 )
    );

    var material = new THREE.LineBasicMaterial( { fog: false } );
    material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );

    this.lightPlane = new THREE.Line( geometry, material );
    this.add( this.lightPlane );

    geometry = new THREE.Geometry();
    geometry.vertices.push(
        new THREE.Vector3(),
        new THREE.Vector3()
    );

    material = new THREE.LineBasicMaterial( { fog: false } );
    material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );

    this.targetLine = new THREE.Line( geometry, material );
    this.add( this.targetLine );

    this.update();

};

THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype );
THREE.DirectionalLightHelper.prototype.constructor = THREE.DirectionalLightHelper;

THREE.DirectionalLightHelper.prototype.dispose = function () {

    this.lightPlane.geometry.dispose();
    this.lightPlane.material.dispose();
    this.targetLine.geometry.dispose();
    this.targetLine.material.dispose();

};

THREE.DirectionalLightHelper.prototype.update = function () {

    var v1 = new THREE.Vector3();
    var v2 = new THREE.Vector3();
    var v3 = new THREE.Vector3();

    return function () {

        v1.setFromMatrixPosition( this.light.matrixWorld );
        v2.setFromMatrixPosition( this.light.target.matrixWorld );
        v3.subVectors( v2, v1 );

        this.lightPlane.lookAt( v3 );
        this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );

        this.targetLine.geometry.vertices[ 1 ].copy( v3 );
        this.targetLine.geometry.verticesNeedUpdate = true;
        this.targetLine.material.color.copy( this.lightPlane.material.color );

    };

}();

// File:src/extras/helpers/EdgesHelper.js

/**
 * @author WestLangley / http://github.com/WestLangley
 * @param object THREE.Mesh whose geometry will be used
 * @param hex line color
 * @param thresholdAngle the minimum angle (in degrees),
 * between the face normals of adjacent faces,
 * that is required to render an edge. A value of 10 means
 * an edge is only rendered if the angle is at least 10 degrees.
 */

THREE.EdgesHelper = function ( object, hex, thresholdAngle ) {

    var color = ( hex !== undefined ) ? hex : 0xffffff;

    THREE.LineSegments.call( this, new THREE.EdgesGeometry( object.geometry, thresholdAngle ), new THREE.LineBasicMaterial( { color: color } ) );

    this.matrix = object.matrixWorld;
    this.matrixAutoUpdate = false;

};

THREE.EdgesHelper.prototype = Object.create( THREE.LineSegments.prototype );
THREE.EdgesHelper.prototype.constructor = THREE.EdgesHelper;

// File:src/extras/helpers/FaceNormalsHelper.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) {

    // FaceNormalsHelper only supports THREE.Geometry

    this.object = object;

    this.size = ( size !== undefined ) ? size : 1;

    var color = ( hex !== undefined ) ? hex : 0xffff00;

    var width = ( linewidth !== undefined ) ? linewidth : 1;

    //

    var nNormals = 0;

    var objGeometry = this.object.geometry;

    if ( objGeometry instanceof THREE.Geometry ) {

        nNormals = objGeometry.faces.length;

    } else {

        console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' );

    }

    //

    var geometry = new THREE.BufferGeometry();

    var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 );

    geometry.addAttribute( 'position', positions );

    THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) );

    //

    this.matrixAutoUpdate = false;
    this.update();

};

THREE.FaceNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype );
THREE.FaceNormalsHelper.prototype.constructor = THREE.FaceNormalsHelper;

THREE.FaceNormalsHelper.prototype.update = ( function () {

    var v1 = new THREE.Vector3();
    var v2 = new THREE.Vector3();
    var normalMatrix = new THREE.Matrix3();

    return function update() {

        this.object.updateMatrixWorld( true );

        normalMatrix.getNormalMatrix( this.object.matrixWorld );

        var matrixWorld = this.object.matrixWorld;

        var position = this.geometry.attributes.position;

        //

        var objGeometry = this.object.geometry;

        var vertices = objGeometry.vertices;

        var faces = objGeometry.faces;

        var idx = 0;

        for ( var i = 0, l = faces.length; i < l; i ++ ) {

            var face = faces[ i ];

            var normal = face.normal;

            v1.copy( vertices[ face.a ] )
                .add( vertices[ face.b ] )
                .add( vertices[ face.c ] )
                .divideScalar( 3 )
                .applyMatrix4( matrixWorld );

            v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );

            position.setXYZ( idx, v1.x, v1.y, v1.z );

            idx = idx + 1;

            position.setXYZ( idx, v2.x, v2.y, v2.z );

            idx = idx + 1;

        }

        position.needsUpdate = true;

        return this;

    }

}() );

// File:src/extras/helpers/GridHelper.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.GridHelper = function ( size, step ) {

    var geometry = new THREE.Geometry();
    var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );

    this.color1 = new THREE.Color( 0x444444 );
    this.color2 = new THREE.Color( 0x888888 );

    for ( var i = - size; i <= size; i += step ) {

        geometry.vertices.push(
            new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ),
            new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size )
        );

        var color = i === 0 ? this.color1 : this.color2;

        geometry.colors.push( color, color, color, color );

    }

    THREE.LineSegments.call( this, geometry, material );

};

THREE.GridHelper.prototype = Object.create( THREE.LineSegments.prototype );
THREE.GridHelper.prototype.constructor = THREE.GridHelper;

THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) {

    this.color1.set( colorCenterLine );
    this.color2.set( colorGrid );

    this.geometry.colorsNeedUpdate = true;

};

// File:src/extras/helpers/HemisphereLightHelper.js

/**
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 */

THREE.HemisphereLightHelper = function ( light, sphereSize ) {

    THREE.Object3D.call( this );

    this.light = light;
    this.light.updateMatrixWorld();

    this.matrix = light.matrixWorld;
    this.matrixAutoUpdate = false;

    this.colors = [ new THREE.Color(), new THREE.Color() ];

    var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
    geometry.rotateX( - Math.PI / 2 );

    for ( var i = 0, il = 8; i < il; i ++ ) {

        geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ];

    }

    var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } );

    this.lightSphere = new THREE.Mesh( geometry, material );
    this.add( this.lightSphere );

    this.update();

};

THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype );
THREE.HemisphereLightHelper.prototype.constructor = THREE.HemisphereLightHelper;

THREE.HemisphereLightHelper.prototype.dispose = function () {

    this.lightSphere.geometry.dispose();
    this.lightSphere.material.dispose();

};

THREE.HemisphereLightHelper.prototype.update = function () {

    var vector = new THREE.Vector3();

    return function () {

        this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity );
        this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity );

        this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() );
        this.lightSphere.geometry.colorsNeedUpdate = true;

    }

}();

// File:src/extras/helpers/PointLightHelper.js

/**
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 */

THREE.PointLightHelper = function ( light, sphereSize ) {

    this.light = light;
    this.light.updateMatrixWorld();

    var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 );
    var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
    material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );

    THREE.Mesh.call( this, geometry, material );

    this.matrix = this.light.matrixWorld;
    this.matrixAutoUpdate = false;

    /*
	var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );
	var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );

	this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
	this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );

	var d = light.distance;

	if ( d === 0.0 ) {

		this.lightDistance.visible = false;

	} else {

		this.lightDistance.scale.set( d, d, d );

	}

	this.add( this.lightDistance );
	*/

};

THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype );
THREE.PointLightHelper.prototype.constructor = THREE.PointLightHelper;

THREE.PointLightHelper.prototype.dispose = function () {

    this.geometry.dispose();
    this.material.dispose();

};

THREE.PointLightHelper.prototype.update = function () {

    this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );

    /*
	var d = this.light.distance;

	if ( d === 0.0 ) {

		this.lightDistance.visible = false;

	} else {

		this.lightDistance.visible = true;
		this.lightDistance.scale.set( d, d, d );

	}
	*/

};

// File:src/extras/helpers/SkeletonHelper.js

/**
 * @author Sean Griffin / http://twitter.com/sgrif
 * @author Michael Guerrero / http://realitymeltdown.com
 * @author mrdoob / http://mrdoob.com/
 * @author ikerr / http://verold.com
 */

THREE.SkeletonHelper = function ( object ) {

    this.bones = this.getBoneList( object );

    var geometry = new THREE.Geometry();

    for ( var i = 0; i < this.bones.length; i ++ ) {

        var bone = this.bones[ i ];

        if ( bone.parent instanceof THREE.Bone ) {

            geometry.vertices.push( new THREE.Vector3() );
            geometry.vertices.push( new THREE.Vector3() );
            geometry.colors.push( new THREE.Color( 0, 0, 1 ) );
            geometry.colors.push( new THREE.Color( 0, 1, 0 ) );

        }

    }

    geometry.dynamic = true;

    var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } );

    THREE.LineSegments.call( this, geometry, material );

    this.root = object;

    this.matrix = object.matrixWorld;
    this.matrixAutoUpdate = false;

    this.update();

};


THREE.SkeletonHelper.prototype = Object.create( THREE.LineSegments.prototype );
THREE.SkeletonHelper.prototype.constructor = THREE.SkeletonHelper;

THREE.SkeletonHelper.prototype.getBoneList = function( object ) {

    var boneList = [];

    if ( object instanceof THREE.Bone ) {

        boneList.push( object );

    }

    for ( var i = 0; i < object.children.length; i ++ ) {

        boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) );

    }

    return boneList;

};

THREE.SkeletonHelper.prototype.update = function () {

    var geometry = this.geometry;

    var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld );

    var boneMatrix = new THREE.Matrix4();

    var j = 0;

    for ( var i = 0; i < this.bones.length; i ++ ) {

        var bone = this.bones[ i ];

        if ( bone.parent instanceof THREE.Bone ) {

            boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld );
            geometry.vertices[ j ].setFromMatrixPosition( boneMatrix );

            boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );
            geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix );

            j += 2;

        }

    }

    geometry.verticesNeedUpdate = true;

    geometry.computeBoundingSphere();

};

// File:src/extras/helpers/SpotLightHelper.js

/**
 * @author alteredq / http://alteredqualia.com/
 * @author mrdoob / http://mrdoob.com/
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.SpotLightHelper = function ( light ) {

    THREE.Object3D.call( this );

    this.light = light;
    this.light.updateMatrixWorld();

    this.matrix = light.matrixWorld;
    this.matrixAutoUpdate = false;

    var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true );

    geometry.translate( 0, - 0.5, 0 );
    geometry.rotateX( - Math.PI / 2 );

    var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );

    this.cone = new THREE.Mesh( geometry, material );
    this.add( this.cone );

    this.update();

};

THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype );
THREE.SpotLightHelper.prototype.constructor = THREE.SpotLightHelper;

THREE.SpotLightHelper.prototype.dispose = function () {

    this.cone.geometry.dispose();
    this.cone.material.dispose();

};

THREE.SpotLightHelper.prototype.update = function () {

    var vector = new THREE.Vector3();
    var vector2 = new THREE.Vector3();

    return function () {

        var coneLength = this.light.distance ? this.light.distance : 10000;
        var coneWidth = coneLength * Math.tan( this.light.angle );

        this.cone.scale.set( coneWidth, coneWidth, coneLength );

        vector.setFromMatrixPosition( this.light.matrixWorld );
        vector2.setFromMatrixPosition( this.light.target.matrixWorld );

        this.cone.lookAt( vector2.sub( vector ) );

        this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );

    };

}();

// File:src/extras/helpers/VertexNormalsHelper.js

/**
 * @author mrdoob / http://mrdoob.com/
 * @author WestLangley / http://github.com/WestLangley
 */

THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) {

    this.object = object;

    this.size = ( size !== undefined ) ? size : 1;

    var color = ( hex !== undefined ) ? hex : 0xff0000;

    var width = ( linewidth !== undefined ) ? linewidth : 1;

    //

    var nNormals = 0;

    var objGeometry = this.object.geometry;

    if ( objGeometry instanceof THREE.Geometry ) {

        nNormals = objGeometry.faces.length * 3;

    } else if ( objGeometry instanceof THREE.BufferGeometry ) {

        nNormals = objGeometry.attributes.normal.count

    }

    //

    var geometry = new THREE.BufferGeometry();

    var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 );

    geometry.addAttribute( 'position', positions );

    THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) );

    //

    this.matrixAutoUpdate = false;

    this.update();

};

THREE.VertexNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype );
THREE.VertexNormalsHelper.prototype.constructor = THREE.VertexNormalsHelper;

THREE.VertexNormalsHelper.prototype.update = ( function () {

    var v1 = new THREE.Vector3();
    var v2 = new THREE.Vector3();
    var normalMatrix = new THREE.Matrix3();

    return function update() {

        var keys = [ 'a', 'b', 'c' ];

        this.object.updateMatrixWorld( true );

        normalMatrix.getNormalMatrix( this.object.matrixWorld );

        var matrixWorld = this.object.matrixWorld;

        var position = this.geometry.attributes.position;

        //

        var objGeometry = this.object.geometry;

        if ( objGeometry instanceof THREE.Geometry ) {

            var vertices = objGeometry.vertices;

            var faces = objGeometry.faces;

            var idx = 0;

            for ( var i = 0, l = faces.length; i < l; i ++ ) {

                var face = faces[ i ];

                for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {

                    var vertex = vertices[ face[ keys[ j ] ] ];

                    var normal = face.vertexNormals[ j ];

                    v1.copy( vertex ).applyMatrix4( matrixWorld );

                    v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );

                    position.setXYZ( idx, v1.x, v1.y, v1.z );

                    idx = idx + 1;

                    position.setXYZ( idx, v2.x, v2.y, v2.z );

                    idx = idx + 1;

                }

            }

        } else if ( objGeometry instanceof THREE.BufferGeometry ) {

            var objPos = objGeometry.attributes.position;

            var objNorm = objGeometry.attributes.normal;

            var idx = 0;

            // for simplicity, ignore index and drawcalls, and render every normal

            for ( var j = 0, jl = objPos.count; j < jl; j ++ ) {

                v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld );

                v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) );

                v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );

                position.setXYZ( idx, v1.x, v1.y, v1.z );

                idx = idx + 1;

                position.setXYZ( idx, v2.x, v2.y, v2.z );

                idx = idx + 1;

            }

        }

        position.needsUpdate = true;

        return this;

    }

}() );

// File:src/extras/helpers/WireframeHelper.js

/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.WireframeHelper = function ( object, hex ) {

    var color = ( hex !== undefined ) ? hex : 0xffffff;

    THREE.LineSegments.call( this, new THREE.WireframeGeometry( object.geometry ), new THREE.LineBasicMaterial( { color: color } ) );

    this.matrix = object.matrixWorld;
    this.matrixAutoUpdate = false;

};

THREE.WireframeHelper.prototype = Object.create( THREE.LineSegments.prototype );
THREE.WireframeHelper.prototype.constructor = THREE.WireframeHelper;

// File:src/extras/objects/ImmediateRenderObject.js

/**
 * @author alteredq / http://alteredqualia.com/
 */

THREE.ImmediateRenderObject = function ( material ) {

    THREE.Object3D.call( this );

    this.material = material;
    this.render = function ( renderCallback ) {};

};

THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype );
THREE.ImmediateRenderObject.prototype.constructor = THREE.ImmediateRenderObject;

// File:src/extras/objects/MorphBlendMesh.js

/**
 * @author alteredq / http://alteredqualia.com/
 */

THREE.MorphBlendMesh = function( geometry, material ) {

    THREE.Mesh.call( this, geometry, material );

    this.animationsMap = {};
    this.animationsList = [];

    // prepare default animation
    // (all frames played together in 1 second)

    var numFrames = this.geometry.morphTargets.length;

    var name = "__default";

    var startFrame = 0;
    var endFrame = numFrames - 1;

    var fps = numFrames / 1;

    this.createAnimation( name, startFrame, endFrame, fps );
    this.setAnimationWeight( name, 1 );

};

THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype );
THREE.MorphBlendMesh.prototype.constructor = THREE.MorphBlendMesh;

THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) {

    var animation = {

        start: start,
        end: end,

        length: end - start + 1,

        fps: fps,
        duration: ( end - start ) / fps,

        lastFrame: 0,
        currentFrame: 0,

        active: false,

        time: 0,
        direction: 1,
        weight: 1,

        directionBackwards: false,
        mirroredLoop: false

    };

    this.animationsMap[ name ] = animation;
    this.animationsList.push( animation );

};

THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) {

    var pattern = /([a-z]+)_?(\d+)/;

    var firstAnimation, frameRanges = {};

    var geometry = this.geometry;

    for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {

        var morph = geometry.morphTargets[ i ];
        var chunks = morph.name.match( pattern );

        if ( chunks && chunks.length > 1 ) {

            var name = chunks[ 1 ];

            if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity };

            var range = frameRanges[ name ];

            if ( i < range.start ) range.start = i;
            if ( i > range.end ) range.end = i;

            if ( ! firstAnimation ) firstAnimation = name;

        }

    }

    for ( var name in frameRanges ) {

        var range = frameRanges[ name ];
        this.createAnimation( name, range.start, range.end, fps );

    }

    this.firstAnimation = firstAnimation;

};

THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) {

    var animation = this.animationsMap[ name ];

    if ( animation ) {

        animation.direction = 1;
        animation.directionBackwards = false;

    }

};

THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) {

    var animation = this.animationsMap[ name ];

    if ( animation ) {

        animation.direction = - 1;
        animation.directionBackwards = true;

    }

};

THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) {

    var animation = this.animationsMap[ name ];

    if ( animation ) {

        animation.fps = fps;
        animation.duration = ( animation.end - animation.start ) / animation.fps;

    }

};

THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) {

    var animation = this.animationsMap[ name ];

    if ( animation ) {

        animation.duration = duration;
        animation.fps = ( animation.end - animation.start ) / animation.duration;

    }

};

THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) {

    var animation = this.animationsMap[ name ];

    if ( animation ) {

        animation.weight = weight;

    }

};

THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) {

    var animation = this.animationsMap[ name ];

    if ( animation ) {

        animation.time = time;

    }

};

THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) {

    var time = 0;

    var animation = this.animationsMap[ name ];

    if ( animation ) {

        time = animation.time;

    }

    return time;

};

THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) {

    var duration = - 1;

    var animation = this.animationsMap[ name ];

    if ( animation ) {

        duration = animation.duration;

    }

    return duration;

};

THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) {

    var animation = this.animationsMap[ name ];

    if ( animation ) {

        animation.time = 0;
        animation.active = true;

    } else {

        console.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" );

    }

};

THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) {

    var animation = this.animationsMap[ name ];

    if ( animation ) {

        animation.active = false;

    }

};

THREE.MorphBlendMesh.prototype.update = function ( delta ) {

    for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {

        var animation = this.animationsList[ i ];

        if ( ! animation.active ) continue;

        var frameTime = animation.duration / animation.length;

        animation.time += animation.direction * delta;

        if ( animation.mirroredLoop ) {

            if ( animation.time > animation.duration || animation.time < 0 ) {

                animation.direction *= - 1;

                if ( animation.time > animation.duration ) {

                    animation.time = animation.duration;
                    animation.directionBackwards = true;

                }

                if ( animation.time < 0 ) {

                    animation.time = 0;
                    animation.directionBackwards = false;

                }

            }

        } else {

            animation.time = animation.time % animation.duration;

            if ( animation.time < 0 ) animation.time += animation.duration;

        }

        var keyframe = animation.start + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
        var weight = animation.weight;

        if ( keyframe !== animation.currentFrame ) {

            this.morphTargetInfluences[ animation.lastFrame ] = 0;
            this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;

            this.morphTargetInfluences[ keyframe ] = 0;

            animation.lastFrame = animation.currentFrame;
            animation.currentFrame = keyframe;

        }

        var mix = ( animation.time % frameTime ) / frameTime;

        if ( animation.directionBackwards ) mix = 1 - mix;

        if ( animation.currentFrame !== animation.lastFrame ) {

            this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
            this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;

        } else {

            this.morphTargetInfluences[ animation.currentFrame ] = weight;

        }

    }

};

